mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-09-12 13:19:30 -04:00
Compare commits
419
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f26f747b80 | ||
|
|
ed2950f867 | ||
|
|
37d0080e92 | ||
|
|
5073e6a575 | ||
|
|
bb8d1c14c5 | ||
|
|
517c35e2db | ||
|
|
ee4f668f29 | ||
|
|
c14e2258b5 | ||
|
|
499ed5e1ab | ||
|
|
c7ca5217a7 | ||
|
|
20cc824592 | ||
|
|
f86309f230 | ||
|
|
ee7c8a5783 | ||
|
|
b29c149529 | ||
|
|
7811f2b1b9 | ||
|
|
f3757a470a | ||
|
|
6c8295fd62 | ||
|
|
a8f80f5a12 | ||
|
|
d8847ff7a8 | ||
|
|
51c5f05771 | ||
|
|
f68facd22f | ||
|
|
9e2e9f3362 | ||
|
|
3786926703 | ||
|
|
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 |
No files matched your search
-23
@@ -1,23 +0,0 @@
|
||||
freebsd_task:
|
||||
name: FreeBSD
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-2
|
||||
env:
|
||||
PATH: /usr/local/bin:$PATH
|
||||
prep_script:
|
||||
- dd if=/dev/zero of=/tmp/zpool bs=1M count=1024
|
||||
- zpool create -m `pwd`/testtmp zpool /tmp/zpool
|
||||
- pkg install -y bash autotools m4 xxhash zstd liblz4 wget
|
||||
- wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
|
||||
configure_script:
|
||||
- CPPFLAGS=-I/usr/local/include/ LDFLAGS=-L/usr/local/lib/ ./configure --disable-md2man
|
||||
make_script:
|
||||
- make
|
||||
install_script:
|
||||
- make install
|
||||
info_script:
|
||||
- rsync --version
|
||||
test_script:
|
||||
- RSYNC_EXPECT_SKIPPED=acls-default,acls,crtimes,protected-regular make check
|
||||
ssl_file_list_script:
|
||||
- rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
@@ -0,0 +1,77 @@
|
||||
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, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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.
|
||||
run: RSYNC_EXPECT_SKIPPED=crtimes make check
|
||||
- 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
|
||||
@@ -1,126 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore: [ .cirrus.yml ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore: [ .cirrus.yml ]
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl wget
|
||||
wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
|
||||
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 make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check29
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ubuntu-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
|
||||
macos-build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: prep
|
||||
run: |
|
||||
brew install automake openssl xxhash zstd lz4 wget
|
||||
sudo pip3 install commonmark
|
||||
wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
|
||||
echo "/usr/local/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: CPPFLAGS=-I/usr/local/opt/openssl/include/ LDFLAGS=-L/usr/local/opt/openssl/lib/ ./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=acls-default,chmod-temp-dir,chown-fake,devices-fake,dir-sgid,protected-regular,xattrs-hlink,xattrs make check
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: macos-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
|
||||
cygwin-build:
|
||||
runs-on: windows-latest
|
||||
if: (github.event_name == 'schedule' || contains(github.event.head_commit.message, '[buildall]'))
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: crazy-max/ghaction-chocolatey@v1.2.2
|
||||
with:
|
||||
args: install -y --no-progress cygwin cyg-get
|
||||
- name: prep
|
||||
run: |
|
||||
cyg-get make autoconf automake gcc-core attr libattr-devel python38 python38-pip libzstd-devel liblz4-devel libssl-devel libxxhash0 libxxhash-devel
|
||||
curl.exe -o git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
|
||||
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
|
||||
run: bash -c 'RSYNC_EXPECT_SKIPPED=acls-default,acls,chown,devices,dir-sgid,protected-regular make check'
|
||||
- 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@v2
|
||||
with:
|
||||
name: cygwin-bin
|
||||
path: |
|
||||
rsync.exe
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Test rsync on Cygwin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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
|
||||
run: bash -c 'RSYNC_EXPECT_SKIPPED=acls-default,acls,bare-do-open-symlink-race,chdir-symlink-race,chmod-symlink-race,chown,daemon-chroot-acl,devices,dir-sgid,open-noatime,protected-regular,sender-flist-symlink-leak,simd-checksum,symlink-dirlink-basis make check'
|
||||
- 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
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Test rsync on FreeBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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
|
||||
./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
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Test rsync on macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=acls-default,chmod-temp-dir,chown-fake,daemon-chroot-acl,devices-fake,dir-sgid,open-noatime,protected-regular,simd-checksum,xattrs-hlink,xattrs make check
|
||||
- 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
|
||||
@@ -0,0 +1,51 @@
|
||||
name: Test rsync on NetBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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
|
||||
./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
|
||||
@@ -0,0 +1,52 @@
|
||||
name: Test rsync on OpenBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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
|
||||
./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
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Test rsync on Solaris
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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
|
||||
./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
|
||||
@@ -0,0 +1,76 @@
|
||||
name: Stable testsuite
|
||||
|
||||
# Regression coverage for the 3.4.x stable branch. The stable branch keeps the
|
||||
# old shell test suite, so the modern Python suite is maintained separately on
|
||||
# the v34-stable-testsuite branch. This job builds rsync from this branch and
|
||||
# runs that suite against the freshly-built binary (the same "testsuite from one
|
||||
# branch, code from another" split fleettest uses). Helper programs and
|
||||
# config.h come from this branch's build (tooldir); the test scripts come from
|
||||
# the stable-testsuite checkout (--srcdir).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ v3.4, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/stable-testsuite.yml'
|
||||
pull_request:
|
||||
branches: [ v3.4, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/stable-testsuite.yml'
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '23 6 * * *'
|
||||
|
||||
jobs:
|
||||
stable-testsuite:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest, ubuntu-22.04 ]
|
||||
name: Stable testsuite on ${{ matrix.os }}
|
||||
steps:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: checkout stable testsuite
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: RsyncProject/rsync
|
||||
ref: v34-stable-testsuite
|
||||
path: stable-testsuite
|
||||
fetch-depth: 1
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc g++ gawk autoconf automake \
|
||||
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 check-progs
|
||||
run: make check-progs
|
||||
- name: info
|
||||
run: ./rsync --version
|
||||
# Pipe transport (the secure stdio default). The TCP-only daemon tests
|
||||
# (daemon-access-ip, proxy-response-line-too-long) skip here and are run in
|
||||
# the --use-tcp pass below; crtimes/daemon-chroot-acl/recv-discard-nullderef
|
||||
# skip on the runner's filesystem / under root.
|
||||
- name: run stable testsuite (pipe)
|
||||
run: |
|
||||
sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef \
|
||||
./stable-testsuite/runtests.py \
|
||||
--srcdir="$GITHUB_WORKSPACE/stable-testsuite" \
|
||||
--rsync-bin="$GITHUB_WORKSPACE/rsync" \
|
||||
-j16
|
||||
# TCP transport over loopback, exercising the daemon paths the pipe run skips.
|
||||
- name: run stable testsuite (tcp)
|
||||
run: |
|
||||
sudo ./stable-testsuite/runtests.py \
|
||||
--srcdir="$GITHUB_WORKSPACE/stable-testsuite" \
|
||||
--rsync-bin="$GITHUB_WORKSPACE/rsync" \
|
||||
--use-tcp -j8
|
||||
@@ -0,0 +1,60 @@
|
||||
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, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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 make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check29
|
||||
- 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
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Test rsync on Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, '*-stable' ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
pull_request:
|
||||
branches: [ master, '*-stable' ]
|
||||
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 make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check29
|
||||
- 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,6 +16,7 @@ aclocal.m4
|
||||
/proto.h
|
||||
/proto.h-tstamp
|
||||
/rsync*.[15]
|
||||
/rrsync
|
||||
/rrsync*.1
|
||||
/rsync*.html
|
||||
/rrsync*.html
|
||||
@@ -57,3 +58,4 @@ aclocal.m4
|
||||
/auto-build-save
|
||||
.deps
|
||||
/*.exe
|
||||
*.dSYM/
|
||||
+9
-4
@@ -13,11 +13,11 @@ 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 & man pages
|
||||
## 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 man pages).
|
||||
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):
|
||||
@@ -26,9 +26,9 @@ build user (after installing python3's pip package):
|
||||
|
||||
You can test if you've got it fixed by running (from the rsync checkout):
|
||||
|
||||
> ./md2man --test rsync-ssl.1.md
|
||||
> ./md-convert --test rsync-ssl.1.md
|
||||
|
||||
Alternately, you can avoid generating the man pages by fetching the very latest
|
||||
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:
|
||||
|
||||
@@ -104,6 +104,8 @@ like.
|
||||
> 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
|
||||
@@ -230,6 +232,9 @@ not completely implement the "New Sockets" API.
|
||||
[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`.
|
||||
|
||||
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
|
||||
|
||||
+62
-36
@@ -30,11 +30,13 @@ SHELL=/bin/sh
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
SIMD_x86_64=simd-checksum-x86_64.o simd-checksum-avx2.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 rsync.1 rsync.1.html \
|
||||
rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.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 \
|
||||
@@ -42,25 +44,26 @@ 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 \
|
||||
util1.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 android.o log.o backup.o delete.o
|
||||
OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
|
||||
usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
|
||||
OBJS3=progress.o pipe.o @ASM@ @SIMD@
|
||||
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
|
||||
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 util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
|
||||
TLS_OBJ = tls.o syscall.o android.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
|
||||
|
||||
# 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:
|
||||
@@ -68,6 +71,8 @@ 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
|
||||
|
||||
@@ -78,12 +83,19 @@ install: all
|
||||
$(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
|
||||
for fn in rsync.1 rsync-ssl.1; do \
|
||||
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man1; \
|
||||
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man1; fi; \
|
||||
done
|
||||
for fn in rsyncd.conf.5; do \
|
||||
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man5; \
|
||||
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man5; fi; \
|
||||
done
|
||||
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; \
|
||||
fn=rrsync.1; \
|
||||
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man1; \
|
||||
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man1; fi; \
|
||||
fi
|
||||
|
||||
install-ssl-daemon: stunnel-rsyncd.conf
|
||||
@@ -146,13 +158,13 @@ git-version.h: ALWAYS_RUN
|
||||
ALWAYS_RUN:
|
||||
|
||||
simd-checksum-x86_64.o: simd-checksum-x86_64.cpp
|
||||
@$(srcdir)/cmd-or-msg disable-simd $(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $(srcdir)/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-asm $(CC) $(CFLAGS) --include=$(srcdir)/rsync.h -DAVX2_ASM -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/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 config.h lib/md-defines.h
|
||||
@$(srcdir)/cmd-or-msg disable-asm $(CC) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/lib/md5-asm-x86_64.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)
|
||||
@@ -166,28 +178,28 @@ getgroups$(EXEEXT): getgroups.o
|
||||
getfsdev$(EXEEXT): getfsdev.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
|
||||
|
||||
TRIMSLASH_OBJ = trimslash.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o
|
||||
TRIMSLASH_OBJ = trimslash.o syscall.o android.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 util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
|
||||
T_UNSAFE_OBJ = t_unsafe.o syscall.o android.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 android.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 android.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 git-version.h
|
||||
|
||||
.PHONY: gensend
|
||||
gensend: gen
|
||||
if ! diff git-version.h $(srcdir)/gists/rsync-git-version.h >/dev/null; then \
|
||||
./rsync -ai git-version.h $(srcdir)/gists/rsync-git-version.h && \
|
||||
(cd $(srcdir)/gists && git commit --allow-empty-message -m '' rsync-git-version.h && git push) ; \
|
||||
fi
|
||||
rsync -aic $(GENFILES) git-version.h $${SAMBA_HOST-samba.org}:/home/ftp/pub/rsync/generated-files/ || true
|
||||
|
||||
aclocal.m4: $(srcdir)/m4/*.m4
|
||||
aclocal -I $(srcdir)/m4
|
||||
|
||||
@@ -271,9 +283,9 @@ rrsync.1: support/rrsync.1.md md-convert Makefile
|
||||
|
||||
.PHONY: clean
|
||||
clean: cleantests
|
||||
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \
|
||||
git-version.h rounding rounding.h *.old rsync*.1 rsync*.5 rsync*.html \
|
||||
daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp
|
||||
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
|
||||
|
||||
.PHONY: cleantests
|
||||
cleantests:
|
||||
@@ -314,22 +326,36 @@ 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.
|
||||
|
||||
# Build the test-helper programs (CHECK_PROGS) without running the suite, so
|
||||
# an external harness (e.g. fleettest.py) can invoke runtests.py with its own
|
||||
# options.
|
||||
.PHONY: check-progs
|
||||
check-progs: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
|
||||
.PHONY: check
|
||||
check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT)
|
||||
|
||||
.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) --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) --protocol=30
|
||||
|
||||
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)
|
||||
|
||||
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/chown-fake.test:
|
||||
ln -s chown.test $(srcdir)/testsuite/chown-fake.test
|
||||
|
||||
@@ -345,7 +371,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`
|
||||
|
||||
# TODO: Add 'dist' target; need to know which files will be included
|
||||
|
||||
@@ -362,4 +388,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/
|
||||
@@ -34,7 +34,7 @@ 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/WayneD/rsync/blob/master/INSTALL.md
|
||||
[1]: https://github.com/RsyncProject/rsync/blob/master/INSTALL.md
|
||||
|
||||
SETUP
|
||||
-----
|
||||
@@ -65,8 +65,8 @@ 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.
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ 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
|
||||
--------------
|
||||
@@ -120,7 +121,7 @@ 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][6] and [on Samba's site][7].
|
||||
|
||||
[6]: https://github.com/WayneD/rsync
|
||||
[6]: https://github.com/RsyncProject/rsync
|
||||
[7]: https://git.samba.org/?p=rsync.git;a=summary
|
||||
|
||||
See [the download page][8] for full details on all the ways to grab the
|
||||
@@ -132,13 +133,12 @@ source.
|
||||
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][9] in this
|
||||
distribution, or at [the Free Software Foundation][10].
|
||||
|
||||
[9]: https://github.com/WayneD/rsync/blob/master/COPYING
|
||||
[9]: https://github.com/RsyncProject/rsync/blob/master/COPYING
|
||||
[10]: https://www.fsf.org/licenses/gpl.html
|
||||
+2
-1
@@ -9,4 +9,5 @@ help backporting fixes into an older release, feel free to ask.
|
||||
|
||||
Email your vulnerability information to rsync's maintainer:
|
||||
|
||||
Wayne Davison <wayne@opencoder.net>
|
||||
Rsync Project <rsync.project@gmail.com>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Routines to authenticate access to a daemon (hosts allow/deny).
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Tridgell
|
||||
* Copyright (C) 2004-2021 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
|
||||
@@ -99,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;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2006-2021 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))
|
||||
@@ -764,6 +765,7 @@ static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode
|
||||
/* 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. */
|
||||
@@ -980,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);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Android-specific helpers.
|
||||
*
|
||||
* openat2() usability probe
|
||||
* -------------------------
|
||||
* openat2(2) is invoked directly via syscall() because the C library lacked a
|
||||
* wrapper for it for years. Under a seccomp filter that uses
|
||||
* SECCOMP_RET_TRAP -- as the Android application sandbox does -- a disallowed
|
||||
* syscall raises SIGSYS and *kills the process* rather than failing with
|
||||
* ENOSYS, so inspecting errno after the call is too late. We therefore probe
|
||||
* openat2() once, behind a temporary SIGSYS handler, so a trapped syscall is
|
||||
* caught and secure_relative_open_linux() can fall back to the portable
|
||||
* per-component O_NOFOLLOW resolver instead of the whole process dying.
|
||||
*
|
||||
* This is only needed on Android, so the probe body is compiled only there.
|
||||
* __ANDROID__ is defined by Bionic's headers and reflects the *target*, not
|
||||
* the build host: it is set both for NDK cross-compiles (from a Linux/macOS
|
||||
* host) and for native Termux builds, and is unset on every other platform.
|
||||
* That makes it a reliable compile-time switch for cross builds -- there is
|
||||
* nothing to detect in configure. Everywhere else openat2() is never
|
||||
* seccomp-trapped to SIGSYS (a missing syscall simply returns ENOSYS), so
|
||||
* openat2_usable() collapses to a constant 1 with no run-time cost.
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
#if defined(__ANDROID__) && defined(HAVE_OPENAT2)
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/openat2.h>
|
||||
|
||||
static sigjmp_buf openat2_probe_env;
|
||||
|
||||
static void openat2_probe_handler(int signo)
|
||||
{
|
||||
(void)signo;
|
||||
siglongjmp(openat2_probe_env, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int openat2_usable(void)
|
||||
{
|
||||
#if defined(__ANDROID__) && defined(HAVE_OPENAT2)
|
||||
static int cached = -1;
|
||||
struct sigaction sa, old_sa;
|
||||
|
||||
if (cached >= 0)
|
||||
return cached;
|
||||
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sa_handler = openat2_probe_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGSYS, &sa, &old_sa) != 0)
|
||||
return cached = 0;
|
||||
|
||||
if (sigsetjmp(openat2_probe_env, 1) != 0) {
|
||||
/* SIGSYS delivered: openat2 is blocked by a seccomp filter. */
|
||||
cached = 0;
|
||||
} else {
|
||||
struct open_how how;
|
||||
int fd;
|
||||
memset(&how, 0, sizeof how);
|
||||
how.flags = O_RDONLY | O_DIRECTORY;
|
||||
how.resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS;
|
||||
fd = syscall(SYS_openat2, AT_FDCWD, ".", &how, sizeof how);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
/* Usable only if the probe actually succeeded. Any failure --
|
||||
* ENOSYS (kernel < 5.6), a seccomp SECCOMP_RET_ERRNO denial
|
||||
* (EPERM/EACCES), or EINVAL (RESOLVE_BENEATH unsupported) --
|
||||
* means we must fall back to the portable O_NOFOLLOW walk. */
|
||||
cached = fd >= 0;
|
||||
}
|
||||
|
||||
sigaction(SIGSYS, &old_sa, NULL);
|
||||
return cached;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
+8
-5
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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"))) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Backup handling code.
|
||||
*
|
||||
* Copyright (C) 1999 Andrew Tridgell
|
||||
* Copyright (C) 2003-2021 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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-2
@@ -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);
|
||||
}
|
||||
|
||||
+330
-152
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
+2
-2
@@ -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-2021 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
|
||||
@@ -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];
|
||||
|
||||
+131
-38
@@ -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-2021 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
|
||||
@@ -30,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;
|
||||
@@ -38,6 +39,7 @@ extern int ignore_errors;
|
||||
extern int preserve_xattrs;
|
||||
extern int kluge_around_eof;
|
||||
extern int munge_symlinks;
|
||||
extern int use_secure_symlinks;
|
||||
extern int open_noatime;
|
||||
extern int sanitize_paths;
|
||||
extern int numeric_ids;
|
||||
@@ -47,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;
|
||||
@@ -66,6 +69,7 @@ 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;
|
||||
@@ -148,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) {
|
||||
@@ -186,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)
|
||||
@@ -288,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--;
|
||||
}
|
||||
@@ -355,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);
|
||||
@@ -403,7 +442,7 @@ 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)
|
||||
@@ -664,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;
|
||||
@@ -789,6 +828,20 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
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... */
|
||||
@@ -925,29 +978,20 @@ 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(module_id)) < 0)
|
||||
@@ -964,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])) {
|
||||
@@ -1017,7 +1070,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
|
||||
io_printf(f_out, "@RSYNCD: OK\n");
|
||||
|
||||
read_args(f_in, name, line, sizeof line, rl_nulls, &argv, &argc, &request);
|
||||
read_args(f_in, name, line, sizeof line, rl_nulls, 1, &argv, &argc, &request);
|
||||
orig_argv = argv;
|
||||
|
||||
save_munge_symlinks = munge_symlinks;
|
||||
@@ -1027,7 +1080,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
if (protect_args && ret) {
|
||||
orig_early_argv = orig_argv;
|
||||
protect_args = 2;
|
||||
read_args(f_in, name, line, sizeof line, 1, &argv, &argc, &request);
|
||||
read_args(f_in, name, line, sizeof line, 1, 0, &argv, &argc, &request);
|
||||
orig_argv = argv;
|
||||
ret = parse_arguments(&argc, (const char ***) &argv);
|
||||
} else
|
||||
@@ -1259,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ 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;
|
||||
@@ -60,13 +61,16 @@ 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 */
|
||||
@@ -79,6 +83,9 @@ 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, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
|
||||
|
||||
@@ -91,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)
|
||||
@@ -123,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))
|
||||
@@ -176,8 +181,8 @@ void set_allow_inc_recurse(void)
|
||||
|
||||
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) {
|
||||
@@ -199,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";
|
||||
@@ -210,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);
|
||||
}
|
||||
}
|
||||
@@ -223,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;
|
||||
}
|
||||
@@ -257,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;
|
||||
}
|
||||
@@ -286,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;
|
||||
@@ -321,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);
|
||||
|
||||
@@ -338,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;
|
||||
@@ -400,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;
|
||||
}
|
||||
@@ -433,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);
|
||||
@@ -464,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;
|
||||
}
|
||||
@@ -523,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);
|
||||
|
||||
@@ -552,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)
|
||||
@@ -604,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) {
|
||||
@@ -801,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
|
||||
}
|
||||
Vendored
+650
-560
File diff suppressed because it is too large.
Load diff
Vendored
+395
-281
File diff suppressed because it is too large.
Load diff
+173
-124
@@ -4,7 +4,6 @@ AC_INIT([rsync],[ ],[https://rsync.samba.org/bug-tracking.html])
|
||||
|
||||
AC_C_BIGENDIAN
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_TIME
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \
|
||||
unistd.h utime.h compat.h sys/param.h ctype.h sys/wait.h sys/stat.h \
|
||||
@@ -13,16 +12,17 @@ AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \
|
||||
netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h mcheck.h \
|
||||
sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \
|
||||
popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netgroup.h \
|
||||
zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h)
|
||||
zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h \
|
||||
bsd/string.h)
|
||||
AC_CHECK_HEADERS([netinet/ip.h], [], [], [[#include <netinet/in.h>]])
|
||||
AC_HEADER_MAJOR_FIXED
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_SRCDIR([byteorder.h])
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_PREREQ([2.69])
|
||||
|
||||
PACKAGE_VERSION=`sed 's/.*"\(.*\)".*/\1/' <$srcdir/version.h`
|
||||
PACKAGE_VERSION=`sed -n 's/.*RSYNC_VERSION.*"\(.*\)".*/\1/p' <$srcdir/version.h`
|
||||
|
||||
AC_MSG_NOTICE([Configuring rsync $PACKAGE_VERSION])
|
||||
|
||||
@@ -60,7 +60,6 @@ AC_PROG_AWK
|
||||
AC_PROG_EGREP
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_MKDIR_P
|
||||
AC_PROG_CC_STDC
|
||||
AC_SUBST(SHELL)
|
||||
AC_PATH_PROG([PERL], [perl])
|
||||
AC_PATH_PROG([PYTHON3], [python3])
|
||||
@@ -83,7 +82,7 @@ if test x"$enable_profile" = x"yes"; then
|
||||
CFLAGS="$CFLAGS -pg"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if md2man can create man pages])
|
||||
AC_MSG_CHECKING([if md2man can create manpages])
|
||||
if test x"$ac_cv_path_PYTHON3" = x; then
|
||||
AC_MSG_RESULT(no - python3 not found)
|
||||
md2man_works=no
|
||||
@@ -101,7 +100,7 @@ fi
|
||||
|
||||
AC_MSG_CHECKING([if we require man-page building])
|
||||
AC_ARG_ENABLE([md2man],
|
||||
AS_HELP_STRING([--disable-md2man],[disable to omit man page creation]))
|
||||
AS_HELP_STRING([--disable-md2man],[disable to omit manpage creation]))
|
||||
if test x"$enable_md2man" != x"no"; then
|
||||
if test -f "$srcdir/rsync.1"; then
|
||||
AC_MSG_RESULT(optional)
|
||||
@@ -109,7 +108,7 @@ if test x"$enable_md2man" != x"no"; then
|
||||
AC_MSG_RESULT(required)
|
||||
if test x"$md2man_works" = x"no"; then
|
||||
err_msg="$err_msg$nl- You need python3 and either the cmarkgfm OR commonmark python3 lib in order"
|
||||
err_msg="$err_msg$nl to build man pages based on the git source (man pages are included in the"
|
||||
err_msg="$err_msg$nl to build manpages based on the git source (manpages are included in the"
|
||||
err_msg="$err_msg$nl official release tar files)."
|
||||
no_lib="$no_lib md2man"
|
||||
fi
|
||||
@@ -135,13 +134,24 @@ if test x"$GCC" = x"yes"; then
|
||||
CFLAGS="$CFLAGS -Wall -W"
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(openssl-conf,
|
||||
AS_HELP_STRING([--with-openssl-conf=PATH],[set default OPENSSL_CONF path for rsync]))
|
||||
case "$with_openssl_conf" in
|
||||
*[^-/a-zA-Z0-9.,=@+_]*) AC_MSG_ERROR([Invalid path given to --with-openssl-conf]) ;;
|
||||
/*) CFLAGS="$CFLAGS -DSET_OPENSSL_CONF=$with_openssl_conf" ;;
|
||||
no|'') ;;
|
||||
yes) AC_MSG_ERROR([No path given to --with-openssl-conf]) ;;
|
||||
*) AC_MSG_ERROR([Non absolute path given to --with-openssl-conf]) ;;
|
||||
esac
|
||||
|
||||
AC_ARG_WITH(rrsync,
|
||||
AS_HELP_STRING([--with-rrsync],[also install the rrsync script and its man page]))
|
||||
AS_HELP_STRING([--with-rrsync],[also install the rrsync script and its manpage]))
|
||||
if test x"$with_rrsync" != x"yes"; then
|
||||
with_rrsync=no
|
||||
else
|
||||
MAKE_RRSYNC='rrsync'
|
||||
MAKE_RRSYNC_1='rrsync.1'
|
||||
GEN_RRSYNC='rrsync.1 rrsync.1.html'
|
||||
fi
|
||||
AC_SUBST(with_rrsync)
|
||||
|
||||
@@ -151,10 +161,10 @@ AC_ARG_WITH(included-popt,
|
||||
AC_ARG_WITH(included-zlib,
|
||||
AS_HELP_STRING([--with-included-zlib],[use bundled zlib library, not from system]))
|
||||
|
||||
AC_ARG_WITH(protected-args,
|
||||
AS_HELP_STRING([--with-protected-args],[make --protected-args option the default]))
|
||||
if test x"$with_protected_args" = x"yes"; then
|
||||
AC_DEFINE_UNQUOTED(RSYNC_USE_PROTECTED_ARGS, 1, [Define to 1 if --protected-args should be the default])
|
||||
AC_ARG_WITH(secluded-args,
|
||||
AS_HELP_STRING([--with-secluded-args],[make --secluded-args option the default]))
|
||||
if test x"$with_secluded_args" = x"yes"; then
|
||||
AC_DEFINE_UNQUOTED(RSYNC_USE_SECLUDED_ARGS, 1, [Define to 1 if --secluded-args should be the default])
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(rsync-path,
|
||||
@@ -228,12 +238,12 @@ fi
|
||||
AC_DEFINE_UNQUOTED(NOBODY_USER, "$NOBODY_USER", [unprivileged user--e.g. nobody])
|
||||
AC_DEFINE_UNQUOTED(NOBODY_GROUP, "$NOBODY_GROUP", [unprivileged group for unprivileged user])
|
||||
|
||||
# SIMD optimizations
|
||||
SIMD=
|
||||
# rolling-checksum SIMD optimizations
|
||||
ROLL_SIMD=
|
||||
|
||||
AC_MSG_CHECKING([whether to enable SIMD optimizations])
|
||||
AC_ARG_ENABLE(simd,
|
||||
AS_HELP_STRING([--enable-simd],[enable/disable to control SIMD optimizations (requires c++)]))
|
||||
AC_MSG_CHECKING([whether to enable rolling-checksum SIMD optimizations])
|
||||
AC_ARG_ENABLE(roll-simd,
|
||||
AS_HELP_STRING([--enable-roll-simd],[enable/disable to control rolling-checksum SIMD optimizations (requires c++)]))
|
||||
|
||||
# Clag is crashing with -g -O2, so we'll get rid of -g for now.
|
||||
CXXFLAGS=`echo "$CXXFLAGS" | sed 's/-g //'`
|
||||
@@ -262,14 +272,14 @@ __attribute__ ((target("ssse3"))) void more_testing(char* buf, int len)
|
||||
}
|
||||
]])
|
||||
|
||||
if test x"$enable_simd" = x""; then
|
||||
if test x"$enable_roll_simd" = x""; then
|
||||
case "$host_os" in
|
||||
*linux*) ;;
|
||||
*) enable_simd=no ;;
|
||||
*) enable_roll_simd=no ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$enable_simd" != x"no"; then
|
||||
if test x"$enable_roll_simd" != x"no"; then
|
||||
# For x86-64 SIMD, g++ >=5 or clang++ >=7 is required
|
||||
if test x"$host_cpu" = x"x86_64" || test x"$host_cpu" = x"amd64"; then
|
||||
AC_LANG(C++)
|
||||
@@ -282,23 +292,23 @@ if test x"$enable_simd" != x"no"; then
|
||||
AC_LANG(C)
|
||||
if test x"$CXX_OK" = x"yes"; then
|
||||
# AC_MSG_RESULT() is called below.
|
||||
SIMD="$host_cpu"
|
||||
elif test x"$enable_simd" = x"yes"; then
|
||||
ROLL_SIMD="$host_cpu"
|
||||
elif test x"$enable_roll_simd" = x"yes"; then
|
||||
AC_MSG_RESULT(error)
|
||||
AC_MSG_ERROR(The SIMD compilation test failed.
|
||||
Omit --enable-simd to continue without it.)
|
||||
AC_MSG_ERROR(The rolling-checksum SIMD compilation test failed.
|
||||
Omit --enable-roll-simd to continue without it.)
|
||||
fi
|
||||
elif test x"$enable_simd" = x"yes"; then
|
||||
elif test x"$enable_roll_simd" = x"yes"; then
|
||||
AC_MSG_RESULT(unavailable)
|
||||
AC_MSG_ERROR(The SIMD optimizations are currently x86_64|amd64 only.
|
||||
Omit --enable-simd to continue without it.)
|
||||
AC_MSG_ERROR(The rolling-checksum SIMD optimizations are currently x86_64|amd64 only.
|
||||
Omit --enable-roll-simd to continue without it.)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$SIMD" != x""; then
|
||||
AC_MSG_RESULT([yes ($SIMD)])
|
||||
AC_DEFINE(HAVE_SIMD, 1, [Define to 1 to enable SIMD optimizations])
|
||||
SIMD='$(SIMD_'"$SIMD)"
|
||||
if test x"$ROLL_SIMD" != x""; then
|
||||
AC_MSG_RESULT([yes ($ROLL_SIMD)])
|
||||
AC_DEFINE(USE_ROLL_SIMD, 1, [Define to 1 to enable rolling-checksum SIMD optimizations])
|
||||
ROLL_SIMD='$(ROLL_SIMD_'"$ROLL_SIMD)"
|
||||
# We only use c++ for its target attribute dispatching, disable unneeded bulky features
|
||||
CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-rtti"
|
||||
# Apple often has "g++" as a symlink for clang. Try to find out the truth.
|
||||
@@ -310,7 +320,7 @@ else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_SUBST(SIMD)
|
||||
AC_SUBST(ROLL_SIMD)
|
||||
|
||||
AC_MSG_CHECKING([if assembler accepts noexecstack])
|
||||
OLD_CFLAGS="$CFLAGS"
|
||||
@@ -321,52 +331,41 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[return 0;]])],
|
||||
CFLAGS="$OLD_CFLAGS"
|
||||
AC_SUBST(NOEXECSTACK)
|
||||
|
||||
ASM=
|
||||
|
||||
AC_MSG_CHECKING([whether to enable ASM optimizations])
|
||||
AC_ARG_ENABLE(asm,
|
||||
AS_HELP_STRING([--enable-asm],[enable/disable to control ASM optimizations]))
|
||||
|
||||
if test x"$enable_asm" = x""; then
|
||||
case "$host_os" in
|
||||
*linux*) ;;
|
||||
*) enable_asm=no ;;
|
||||
esac
|
||||
dnl Only define HAVE_OPENAT2 when both the <linux/openat2.h> header and the
|
||||
dnl SYS_openat2 syscall number are present. syscall.c uses openat2(RESOLVE_BENEATH)
|
||||
dnl for the secure resolver on Linux 5.6+; defining it unconditionally broke the
|
||||
dnl build on older kernels/headers that lack the header (#924, #905, #900).
|
||||
AC_CACHE_CHECK([for openat2],rsync_cv_HAVE_OPENAT2,[
|
||||
AC_COMPILE_IFELSE([
|
||||
AC_LANG_PROGRAM([[
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/openat2.h>
|
||||
]], [[
|
||||
struct open_how how;
|
||||
how.resolve = RESOLVE_BENEATH;
|
||||
return SYS_openat2 + (int)how.resolve;
|
||||
]])
|
||||
],
|
||||
[rsync_cv_HAVE_OPENAT2=yes], [rsync_cv_HAVE_OPENAT2=no])
|
||||
])
|
||||
if test x"$rsync_cv_HAVE_OPENAT2" = x"yes"; then
|
||||
AC_DEFINE([HAVE_OPENAT2], 1,
|
||||
[Define to use Linux openat2(RESOLVE_BENEATH) in secure_relative_open where available.])
|
||||
fi
|
||||
|
||||
if test x"$enable_asm" != x"no"; then
|
||||
if test x"$host_cpu" = x"x86_64" || test x"$host_cpu" = x"amd64"; then
|
||||
ASM="$host_cpu"
|
||||
elif test x"$enable_asm" = x"yes"; then
|
||||
AC_MSG_RESULT(unavailable)
|
||||
AC_MSG_ERROR(The ASM optimizations are currently x86_64|amd64 only.
|
||||
Omit --enable-asm to continue without it.)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$ASM" != x""; then
|
||||
AC_MSG_RESULT([yes ($ASM)])
|
||||
AC_DEFINE(HAVE_ASM, 1, [Define to 1 to enable ASM optimizations])
|
||||
ASM='$(ASM_'"$ASM)"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_SUBST(ASM)
|
||||
|
||||
# arrgh. libc in some old debian version screwed up the largefile
|
||||
# stuff, getting byte range locking wrong
|
||||
AC_CACHE_CHECK([for broken largefile support],rsync_cv_HAVE_BROKEN_LARGEFILE,[
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([[
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
$ac_includes_default
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#elif defined HAVE_SYS_FCNTL_H
|
||||
# include <sys/fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
@@ -411,22 +410,22 @@ AS_HELP_STRING([--disable-ipv6],[disable to omit ipv6 support]),
|
||||
;;
|
||||
esac ],
|
||||
|
||||
AC_TRY_RUN([ /* AF_INET6 avalable check */
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([[ /* AF_INET6 availability check */
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
main()
|
||||
int main()
|
||||
{
|
||||
if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
|
||||
exit(1);
|
||||
else
|
||||
exit(0);
|
||||
}
|
||||
],
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(INET6, 1, [true if you have IPv6]),
|
||||
AC_MSG_RESULT(no),
|
||||
AC_MSG_RESULT(no)
|
||||
]])],
|
||||
[AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(INET6, 1, true if you have IPv6)],
|
||||
[AC_MSG_RESULT(no)],
|
||||
[AC_MSG_RESULT(no)]
|
||||
))
|
||||
|
||||
dnl Do you want to disable use of locale functions
|
||||
@@ -447,6 +446,26 @@ case $host_os in
|
||||
* ) AC_MSG_RESULT(no);;
|
||||
esac
|
||||
|
||||
# We default to using our zlib unless --with-included-zlib=no is given.
|
||||
if test x"$with_included_zlib" != x"no"; then
|
||||
with_included_zlib=yes
|
||||
elif test x"$ac_cv_header_zlib_h" != x"yes"; then
|
||||
with_included_zlib=yes
|
||||
fi
|
||||
if test x"$with_included_zlib" != x"yes"; then
|
||||
AC_CHECK_LIB(z, deflateParams, , [with_included_zlib=yes])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to use included zlib])
|
||||
if test x"$with_included_zlib" = x"yes"; then
|
||||
AC_MSG_RESULT($srcdir/zlib)
|
||||
BUILD_ZLIB='$(zlib_OBJS)'
|
||||
CFLAGS="-I$srcdir/zlib $CFLAGS"
|
||||
else
|
||||
AC_DEFINE(EXTERNAL_ZLIB, 1, [Define to 1 if using external zlib])
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to enable use of openssl crypto library])
|
||||
AC_ARG_ENABLE([openssl],
|
||||
AS_HELP_STRING([--disable-openssl],[disable to omit openssl crypto library]))
|
||||
@@ -455,19 +474,77 @@ AH_TEMPLATE([USE_OPENSSL],
|
||||
if test x"$enable_openssl" != x"no"; then
|
||||
if test x"$ac_cv_header_openssl_md4_h" = x"yes" && test x"$ac_cv_header_openssl_md5_h" = x"yes"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_SEARCH_LIBS(MD5_Init, crypto,
|
||||
[AC_DEFINE(USE_OPENSSL)],
|
||||
[err_msg="$err_msg$nl- Failed to find MD5_Init function in openssl crypto lib.";
|
||||
AC_SEARCH_LIBS(EVP_MD_CTX_copy, crypto,
|
||||
[AC_DEFINE(USE_OPENSSL)
|
||||
enable_openssl=yes],
|
||||
[err_msg="$err_msg$nl- Failed to find EVP_MD_CTX_copy function in openssl crypto lib.";
|
||||
no_lib="$no_lib openssl"])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
err_msg="$err_msg$nl- Failed to find openssl/md4.h and openssl/md5.h for openssl crypto lib support."
|
||||
no_lib="$no_lib openssl"
|
||||
fi
|
||||
if test x"$enable_md5_asm" != x"yes"; then
|
||||
enable_md5_asm=no
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
MD5_ASM=
|
||||
|
||||
AC_MSG_CHECKING([whether to enable MD5 ASM optimizations])
|
||||
AC_ARG_ENABLE(md5-asm,
|
||||
AS_HELP_STRING([--enable-md5-asm],[enable/disable to control MD5 ASM optimizations]))
|
||||
|
||||
if test x"$enable_md5_asm" = x""; then
|
||||
case "$host_os" in
|
||||
*linux*) ;;
|
||||
*) enable_md5_asm=no ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$enable_md5_asm" != x"no"; then
|
||||
if test x"$host_cpu" = x"x86_64" || test x"$host_cpu" = x"amd64"; then
|
||||
MD5_ASM="$host_cpu"
|
||||
elif test x"$enable_md5_asm" = x"yes"; then
|
||||
AC_MSG_RESULT(unavailable)
|
||||
AC_MSG_ERROR(The ASM optimizations are currently x86_64|amd64 only.
|
||||
Omit --enable-md5-asm to continue without it.)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$MD5_ASM" != x""; then
|
||||
AC_MSG_RESULT([yes ($MD5_ASM)])
|
||||
AC_DEFINE(USE_MD5_ASM, 1, [Define to 1 to enable MD5 ASM optimizations])
|
||||
MD5_ASM='$(MD5_ASM_'"$MD5_ASM)"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_SUBST(MD5_ASM)
|
||||
|
||||
ROLL_ASM=
|
||||
|
||||
AC_MSG_CHECKING([whether to enable rolling-checksum ASM optimizations])
|
||||
AC_ARG_ENABLE(roll-asm,
|
||||
AS_HELP_STRING([--enable-roll-asm],[enable/disable to control rolling-checksum ASM optimizations (requires --enable-roll-simd)]))
|
||||
|
||||
if test x"$ROLL_SIMD" = x""; then
|
||||
enable_roll_asm=no
|
||||
fi
|
||||
|
||||
if test x"$enable_roll_asm" = x"yes"; then
|
||||
ROLL_ASM="$host_cpu"
|
||||
AC_MSG_RESULT([yes ($ROLL_ASM)])
|
||||
AC_DEFINE(USE_ROLL_ASM, 1, [Define to 1 to enable rolling-checksum ASM optimizations (requires --enable-roll-simd)])
|
||||
ROLL_ASM='$(ROLL_ASM_'"$ROLL_ASM)"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_SUBST(ROLL_ASM)
|
||||
|
||||
AC_MSG_CHECKING([whether to enable xxhash checksum support])
|
||||
AC_ARG_ENABLE([xxhash],
|
||||
AS_HELP_STRING([--disable-xxhash],[disable to omit xxhash checksums]))
|
||||
@@ -491,7 +568,7 @@ fi
|
||||
|
||||
AC_MSG_CHECKING([whether to enable zstd compression])
|
||||
AC_ARG_ENABLE([zstd],
|
||||
AC_HELP_STRING([--disable-zstd], [disable to omit zstd compression]))
|
||||
AS_HELP_STRING([--disable-zstd], [disable to omit zstd compression]))
|
||||
AH_TEMPLATE([SUPPORT_ZSTD],
|
||||
[Undefine if you do not want zstd compression. By default this is defined.])
|
||||
if test x"$enable_zstd" != x"no"; then
|
||||
@@ -512,7 +589,7 @@ fi
|
||||
|
||||
AC_MSG_CHECKING([whether to enable LZ4 compression])
|
||||
AC_ARG_ENABLE([lz4],
|
||||
AC_HELP_STRING([--disable-lz4], [disable to omit LZ4 compression]))
|
||||
AS_HELP_STRING([--disable-lz4], [disable to omit LZ4 compression]))
|
||||
AH_TEMPLATE([SUPPORT_LZ4],
|
||||
[Undefine if you do not want LZ4 compression. By default this is defined.])
|
||||
if test x"$enable_lz4" != x"no"; then
|
||||
@@ -537,8 +614,8 @@ if test x"$no_lib" != x; then
|
||||
echo "$err_msg"
|
||||
echo ""
|
||||
echo "See the INSTALL file for hints on how to install the missing libraries and/or"
|
||||
echo "how to generate (or fetch) man pages:"
|
||||
echo " https://github.com/WayneD/rsync/blob/master/INSTALL.md"
|
||||
echo "how to generate (or fetch) manpages:"
|
||||
echo " https://github.com/RsyncProject/rsync/blob/master/INSTALL.md"
|
||||
echo ""
|
||||
echo "To disable one or more features, the relevant configure options are:"
|
||||
for lib in $no_lib; do
|
||||
@@ -598,7 +675,11 @@ fi
|
||||
|
||||
AC_TYPE_UID_T
|
||||
AC_CHECK_TYPES([mode_t,off_t,size_t,pid_t,id_t])
|
||||
AC_TYPE_GETGROUPS
|
||||
if test "$cross_compiling" = no; then
|
||||
AC_TYPE_GETGROUPS
|
||||
else
|
||||
AC_DEFINE([GETGROUPS_T],[gid_t],[Define to the type of elements in the array set by `getgroups'. Usually this is either `int' or `gid_t'.])
|
||||
fi
|
||||
AC_CHECK_MEMBERS([struct stat.st_rdev,
|
||||
struct stat.st_mtimensec,
|
||||
struct stat.st_mtimespec.tv_nsec,
|
||||
@@ -829,9 +910,9 @@ AC_FUNC_UTIME_NULL
|
||||
AC_FUNC_ALLOCA
|
||||
AC_CHECK_FUNCS(waitpid wait4 getcwd chown chmod lchmod mknod mkfifo \
|
||||
fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
|
||||
chflags getattrlist mktime innetgr linkat \
|
||||
chflags getattrlist mktime innetgr linkat mknodat mkfifoat \
|
||||
memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
|
||||
strlcat strlcpy strtol mallinfo mallinfo2 getgroups setgroups geteuid getegid \
|
||||
strlcat strlcpy stpcpy strtol mallinfo mallinfo2 getgroups setgroups geteuid getegid \
|
||||
setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
|
||||
seteuid strerror putenv iconv_open locale_charset nl_langinfo getxattr \
|
||||
extattr_get_link sigaction sigprocmask setattrlist getgrouplist \
|
||||
@@ -1040,26 +1121,13 @@ elif test x"$ac_cv_header_popt_h" != x"yes"; then
|
||||
with_included_popt=yes
|
||||
fi
|
||||
|
||||
if test x"$GCC" = x"yes"; then
|
||||
if test x"$with_included_popt" != x"yes"; then
|
||||
# Turn pedantic warnings into errors to ensure an array-init overflow is an error.
|
||||
CFLAGS="$CFLAGS -pedantic-errors"
|
||||
else
|
||||
# Our internal popt code cannot be compiled with pedantic warnings as errors, so try to
|
||||
# turn off pedantic warnings (which will not lose the error for array-init overflow).
|
||||
# Older gcc versions don't understand -Wno-pedantic, so check if --help=warnings lists
|
||||
# -Wpedantic and use that as a flag.
|
||||
case `$CC --help=warnings 2>/dev/null | grep Wpedantic` in
|
||||
*-Wpedantic*) CFLAGS="$CFLAGS -pedantic-errors -Wno-pedantic" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to use included libpopt])
|
||||
if test x"$with_included_popt" = x"yes"; then
|
||||
AC_MSG_RESULT($srcdir/popt)
|
||||
BUILD_POPT='$(popt_OBJS)'
|
||||
CFLAGS="-I$srcdir/popt $CFLAGS"
|
||||
AC_DEFINE(POPT_SYSCONFDIR, "/etc", [sysconfig dir for popt])
|
||||
AC_DEFINE(PACKAGE, "rsync", [package name for rsync])
|
||||
if test x"$ALLOCA" != x
|
||||
then
|
||||
# this can be removed when/if we add an included alloca.c;
|
||||
@@ -1070,28 +1138,8 @@ else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
# We default to using our zlib unless --with-included-zlib=no is given.
|
||||
if test x"$with_included_zlib" != x"no"; then
|
||||
with_included_zlib=yes
|
||||
elif test x"$ac_cv_header_zlib_h" != x"yes"; then
|
||||
with_included_zlib=yes
|
||||
fi
|
||||
if test x"$with_included_zlib" != x"yes"; then
|
||||
AC_CHECK_LIB(z, deflateParams, , [with_included_zlib=yes])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to use included zlib])
|
||||
if test x"$with_included_zlib" = x"yes"; then
|
||||
AC_MSG_RESULT($srcdir/zlib)
|
||||
BUILD_ZLIB='$(zlib_OBJS)'
|
||||
CFLAGS="-I$srcdir/zlib $CFLAGS"
|
||||
else
|
||||
AC_DEFINE(EXTERNAL_ZLIB, 1, [Define to 1 if using external zlib])
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for unsigned char],rsync_cv_SIGNED_CHAR_OK,[
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[signed char *s = ""]])],[rsync_cv_SIGNED_CHAR_OK=yes],[rsync_cv_SIGNED_CHAR_OK=no])])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[signed char *s = (signed char *)""]])],[rsync_cv_SIGNED_CHAR_OK=yes],[rsync_cv_SIGNED_CHAR_OK=no])])
|
||||
if test x"$rsync_cv_SIGNED_CHAR_OK" = x"yes"; then
|
||||
AC_DEFINE(SIGNED_CHAR_OK, 1, [Define to 1 if "signed char" is a valid type])
|
||||
fi
|
||||
@@ -1257,6 +1305,7 @@ AC_SUBST(BUILD_POPT)
|
||||
AC_SUBST(BUILD_ZLIB)
|
||||
AC_SUBST(MAKE_RRSYNC)
|
||||
AC_SUBST(MAKE_RRSYNC_1)
|
||||
AC_SUBST(GEN_RRSYNC)
|
||||
AC_SUBST(MAKE_MAN)
|
||||
|
||||
AC_CHECK_FUNCS(_acl __acl _facl __facl)
|
||||
@@ -1365,7 +1414,7 @@ else
|
||||
AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs (or equivalent)])
|
||||
AC_DEFINE(SUPPORT_XATTRS, 1)
|
||||
AC_DEFINE(NO_SYMLINK_USER_XATTRS, 1, [True if symlinks do not support user xattrs])
|
||||
AC_CHECK_LIB(attr,getxattr)
|
||||
AC_SEARCH_LIBS(getxattr,attr)
|
||||
;;
|
||||
darwin*)
|
||||
AC_MSG_RESULT(Using OS X xattrs)
|
||||
|
||||
+38
-16
@@ -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
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
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 struct parm_struct parm_table[] = {"
|
||||
parms = "\nstatic const struct parm_struct parm_table[] = {"
|
||||
comment_fmt = "\n/********** %s **********/\n"
|
||||
tdstruct = "typedef struct {"
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,9 +60,9 @@ BOOL read_only True
|
||||
BOOL reverse_lookup True
|
||||
BOOL strict_modes True
|
||||
BOOL transfer_logging False
|
||||
BOOL use_chroot True
|
||||
BOOL write_only False
|
||||
|
||||
BOOL3 munge_symlinks Unset
|
||||
BOOL3 numeric_ids Unset
|
||||
BOOL3 open_noatime Unset
|
||||
BOOL3 use_chroot Unset
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -25,16 +25,21 @@
|
||||
|
||||
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];
|
||||
@@ -44,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)
|
||||
@@ -70,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
|
||||
@@ -152,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
|
||||
@@ -208,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) {
|
||||
@@ -288,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)
|
||||
{
|
||||
@@ -438,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));
|
||||
@@ -452,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;
|
||||
@@ -621,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))
|
||||
@@ -702,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();
|
||||
@@ -714,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);
|
||||
}
|
||||
@@ -740,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,
|
||||
@@ -886,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 */
|
||||
@@ -1294,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;
|
||||
@@ -1309,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)) {
|
||||
@@ -1351,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. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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-2021 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;
|
||||
@@ -72,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;
|
||||
@@ -130,6 +132,18 @@ static int64 tmp_dev = -1, tmp_ino;
|
||||
#endif
|
||||
static char tmp_sum[MAX_DIGEST_LEN];
|
||||
|
||||
#ifdef ST_MTIME_NSEC
|
||||
/* Return st_mtim nsec if it is in the wire-valid range, else 0. */
|
||||
static inline uint32 wire_mtime_nsec_from_stat(const STRUCT_STAT *stp)
|
||||
{
|
||||
unsigned long nsec = (unsigned long)stp->ST_MTIME_NSEC;
|
||||
|
||||
if (nsec > MAX_WIRE_NSEC)
|
||||
return 0;
|
||||
return (uint32)nsec;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char empty_sum[MAX_DIGEST_LEN];
|
||||
static int flist_count_offset; /* for --delete --progress */
|
||||
static int show_filelist_progress;
|
||||
@@ -144,7 +158,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;
|
||||
}
|
||||
@@ -700,6 +715,7 @@ 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;
|
||||
@@ -752,7 +768,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)
|
||||
@@ -814,6 +830,7 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
linkname_len = strlen(F_SYMLINK(first)) + 1;
|
||||
else
|
||||
linkname_len = 0;
|
||||
real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
|
||||
goto create_object;
|
||||
}
|
||||
}
|
||||
@@ -831,13 +848,13 @@ 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)
|
||||
#ifndef CAN_SET_NSEC
|
||||
(void)read_varint(f);
|
||||
(void)read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
#else
|
||||
modtime_nsec = read_varint(f);
|
||||
modtime_nsec = read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
else
|
||||
modtime_nsec = 0;
|
||||
#endif
|
||||
@@ -856,8 +873,24 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (!(xflags & XMIT_SAME_MODE))
|
||||
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. mode 0 is the one
|
||||
* legitimate exception: --delete-missing-args (missing_args==2)
|
||||
* sends a missing arg as a mode-0 entry (IS_MISSING_FILE), the
|
||||
* generator's delete signal (#910). */
|
||||
if (mode != 0 || missing_args != 2) {
|
||||
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
|
||||
@@ -941,10 +974,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;
|
||||
@@ -973,6 +1016,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 */
|
||||
@@ -1160,8 +1216,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. */
|
||||
@@ -1211,7 +1267,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
|
||||
int extra_len = file_extra_cnt * EXTRA_LEN;
|
||||
const char *basename;
|
||||
alloc_pool_t *pool;
|
||||
STRUCT_STAT st;
|
||||
STRUCT_STAT st = {0};
|
||||
char *bp;
|
||||
|
||||
if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) {
|
||||
@@ -1360,9 +1416,25 @@ 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;
|
||||
{
|
||||
uint32 nsec = wire_mtime_nsec_from_stat(&st);
|
||||
|
||||
if (nsec && protocol_version >= 31)
|
||||
extra_len += EXTRA_LEN;
|
||||
}
|
||||
#endif
|
||||
#if SIZEOF_CAPITAL_OFF_T >= 8
|
||||
if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
|
||||
@@ -1417,9 +1489,13 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
|
||||
file->flags = flags;
|
||||
file->modtime = st.st_mtime;
|
||||
#ifdef ST_MTIME_NSEC
|
||||
if (st.ST_MTIME_NSEC && protocol_version >= 31) {
|
||||
file->flags |= FLAG_MOD_NSEC;
|
||||
F_MOD_NSEC(file) = st.ST_MTIME_NSEC;
|
||||
{
|
||||
uint32 nsec = wire_mtime_nsec_from_stat(&st);
|
||||
|
||||
if (nsec && protocol_version >= 31) {
|
||||
file->flags |= FLAG_MOD_NSEC;
|
||||
F_MOD_NSEC(file) = nsec;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
file->len32 = (uint32)st.st_size;
|
||||
@@ -2019,10 +2095,9 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
|
||||
}
|
||||
|
||||
if (name_type != NORMAL_NAME) {
|
||||
STRUCT_STAT st;
|
||||
if (name_type == MISSING_NAME)
|
||||
memset(&st, 0, sizeof st);
|
||||
else if (link_stat(fbuf, &st, 1) != 0) {
|
||||
STRUCT_STAT st = {0};
|
||||
|
||||
if (name_type != MISSING_NAME && link_stat(fbuf, &st, 1) != 0) {
|
||||
interpret_stat_error(fbuf, True);
|
||||
continue;
|
||||
}
|
||||
@@ -2154,7 +2229,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
|
||||
static const char *lastdir;
|
||||
static int lastdir_len = -1;
|
||||
int len, dirlen;
|
||||
STRUCT_STAT st;
|
||||
STRUCT_STAT st = {0};
|
||||
char *p, *dir;
|
||||
struct file_list *flist;
|
||||
struct timeval start_tv, end_tv;
|
||||
@@ -2327,7 +2402,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;
|
||||
@@ -2544,6 +2619,19 @@ 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);
|
||||
|
||||
@@ -2602,7 +2690,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;
|
||||
}
|
||||
@@ -2619,7 +2707,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++;
|
||||
|
||||
@@ -3114,8 +3202,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
|
||||
|
||||
+65
-39
@@ -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-2021 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,11 +35,11 @@ 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;
|
||||
@@ -66,6 +66,7 @@ extern int inplace;
|
||||
extern int append_mode;
|
||||
extern int make_backups;
|
||||
extern int csum_length;
|
||||
extern int xfer_sum_len;
|
||||
extern int ignore_times;
|
||||
extern int size_only;
|
||||
extern OFF_T max_size;
|
||||
@@ -229,11 +230,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;
|
||||
|
||||
@@ -247,6 +250,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)
|
||||
@@ -532,7 +539,7 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
|
||||
iflags |= ITEM_REPORT_CRTIME;
|
||||
}
|
||||
#endif
|
||||
#if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
|
||||
#ifndef CAN_CHMOD_SYMLINK
|
||||
if (S_ISLNK(file->mode)) {
|
||||
;
|
||||
} else
|
||||
@@ -691,6 +698,11 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
|
||||
{
|
||||
int32 blength;
|
||||
int s2length;
|
||||
/* The strong sum can be no longer than the negotiated checksum digest:
|
||||
* a short checksum (e.g. xxh64 = 8 bytes, when xxh128/xxh3 are absent)
|
||||
* makes xfer_sum_len < SUM_LENGTH, and the sender rejects an s2length
|
||||
* larger than xfer_sum_len (io.c). */
|
||||
int max_s2length = MIN(SUM_LENGTH, xfer_sum_len);
|
||||
int64 l;
|
||||
|
||||
if (len < 0) {
|
||||
@@ -725,7 +737,7 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
|
||||
if (protocol_version < 27) {
|
||||
s2length = csum_length;
|
||||
} else if (csum_length == SUM_LENGTH) {
|
||||
s2length = SUM_LENGTH;
|
||||
s2length = max_s2length;
|
||||
} else {
|
||||
int32 c;
|
||||
int b = BLOCKSUM_BIAS;
|
||||
@@ -734,7 +746,7 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
|
||||
/* add a bit, subtract rollsum, round up. */
|
||||
s2length = (b + 1 - 32 + 7) / 8; /* --optimize in compiler-- */
|
||||
s2length = MAX(s2length, csum_length);
|
||||
s2length = MIN(s2length, SUM_LENGTH);
|
||||
s2length = MIN(s2length, max_s2length);
|
||||
}
|
||||
|
||||
sum->flength = len;
|
||||
@@ -783,7 +795,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;
|
||||
@@ -875,9 +887,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));
|
||||
@@ -981,7 +996,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
|
||||
@@ -1109,7 +1124,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);
|
||||
@@ -1312,7 +1327,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
}
|
||||
if (relative_paths && !implied_dirs && file->mode != 0
|
||||
&& do_stat(dn, &sx.st) < 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) {
|
||||
@@ -1424,7 +1439,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
&& (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;
|
||||
}
|
||||
@@ -1466,10 +1481,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));
|
||||
@@ -1496,7 +1511,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));
|
||||
@@ -1703,7 +1718,8 @@ 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 && stype == ftype
|
||||
&& file->modtime - sx.st.st_mtime < modify_window) {
|
||||
if (INFO_GTE(SKIP, 1))
|
||||
rprintf(FINFO, "%s is newer\n", fname);
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
@@ -1793,13 +1809,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 (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);
|
||||
@@ -1813,7 +1835,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;
|
||||
}
|
||||
|
||||
@@ -1858,7 +1880,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:
|
||||
@@ -1875,11 +1897,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) {
|
||||
@@ -1887,14 +1907,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;
|
||||
@@ -1945,7 +1963,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,
|
||||
@@ -1953,10 +1970,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)
|
||||
@@ -2011,7 +2029,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;
|
||||
@@ -2027,7 +2045,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;
|
||||
@@ -2035,10 +2053,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;
|
||||
}
|
||||
}
|
||||
@@ -2102,7 +2124,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)) {
|
||||
@@ -2137,6 +2159,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)
|
||||
@@ -2165,6 +2189,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)
|
||||
@@ -2365,7 +2391,7 @@ void generate_files(int f_out, const char *local_name)
|
||||
write_ndx(f_out, NDX_DONE);
|
||||
|
||||
if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG()) {
|
||||
if ((INFO_GTE(STATS, 2) && (delete_mode || force_delete)) || read_batch)
|
||||
if (delete_mode || force_delete || read_batch)
|
||||
write_del_stats(f_out);
|
||||
if (EARLY_DELAY_DONE_MSG()) /* Can't send this before delay */
|
||||
write_ndx(f_out, NDX_DONE);
|
||||
@@ -2410,7 +2436,7 @@ void generate_files(int f_out, const char *local_name)
|
||||
|
||||
if (protocol_version >= 31) {
|
||||
if (!EARLY_DELETE_DONE_MSG()) {
|
||||
if (INFO_GTE(STATS, 2) || read_batch)
|
||||
if (delete_mode || force_delete || read_batch)
|
||||
write_del_stats(f_out);
|
||||
write_ndx(f_out, NDX_DONE);
|
||||
}
|
||||
|
||||
+169
-5
@@ -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
|
||||
@@ -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]];
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Inline functions for rsync.
|
||||
*
|
||||
* Copyright (C) 2007-2021 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
|
||||
|
||||
@@ -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,6 +55,7 @@ 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;
|
||||
@@ -84,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;
|
||||
@@ -113,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 */
|
||||
@@ -376,6 +380,7 @@ 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;
|
||||
}
|
||||
@@ -419,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'. */
|
||||
@@ -434,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)
|
||||
@@ -450,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
|
||||
@@ -1057,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]);
|
||||
@@ -1071,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
|
||||
@@ -1125,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'
|
||||
@@ -1256,8 +1292,21 @@ int read_line(int fd, char *buf, size_t bufsiz, int flags)
|
||||
return s - buf;
|
||||
}
|
||||
|
||||
/* Reverse safe_arg()'s backslash escaping of a daemon option arg, the way a
|
||||
* remote shell un-escapes args for the ssh transport. In place; \X -> X. */
|
||||
static void unbackslash_arg(char *s)
|
||||
{
|
||||
char *f = s, *t = s;
|
||||
while (*f) {
|
||||
if (*f == '\\' && f[1])
|
||||
f++;
|
||||
*t++ = *f++;
|
||||
}
|
||||
*t = '\0';
|
||||
}
|
||||
|
||||
void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
|
||||
char ***argv_p, int *argc_p, char **request_p)
|
||||
int unescape, char ***argv_p, int *argc_p, char **request_p)
|
||||
{
|
||||
int maxargs = MAX_ARGS;
|
||||
int dot_pos = 0, argc = 0, request_len = 0;
|
||||
@@ -1299,6 +1348,11 @@ void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
|
||||
glob_expand(buf, &argv, &argc, &maxargs);
|
||||
} else {
|
||||
p = strdup(buf);
|
||||
/* An option arg the client escaped with safe_arg() (no
|
||||
* remote shell un-escapes it for a daemon). File args
|
||||
* after the dot are handled by glob_expand() below. */
|
||||
if (unescape)
|
||||
unbackslash_arg(p);
|
||||
argv[argc++] = p;
|
||||
if (*p == '.' && p[1] == '\0')
|
||||
dot_pos = argc;
|
||||
@@ -1567,14 +1621,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);
|
||||
@@ -1751,6 +1806,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 {
|
||||
@@ -1824,6 +1886,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
|
||||
@@ -1843,6 +1944,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) {
|
||||
@@ -1929,6 +2031,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",
|
||||
@@ -1936,7 +2053,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);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Inline functions for rsync.
|
||||
*
|
||||
* Copyright (C) 2007-2021 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
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define LATEST_YEAR "2022"
|
||||
#define LATEST_YEAR "2026"
|
||||
@@ -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 */
|
||||
@@ -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 /* } */
|
||||
+2
-9
@@ -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
|
||||
+5
-4
@@ -9,7 +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)(); /* called if malloc fails */
|
||||
void (*bomb)(const char*, const char*, int); /* called if malloc fails */
|
||||
int flags;
|
||||
|
||||
/* statistical data */
|
||||
@@ -42,6 +42,7 @@ 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*, const char*, int), int flags)
|
||||
@@ -100,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);
|
||||
@@ -235,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);
|
||||
@@ -292,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);
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+89
-89
@@ -2,7 +2,7 @@
|
||||
* Unix SMB/CIFS implementation.
|
||||
* Based on the Samba ACL support code.
|
||||
* Copyright (C) Jeremy Allison 2000.
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* The permission functions have been changed to get/set all bits via
|
||||
* one call. Some functions that rsync doesn't need were also removed.
|
||||
@@ -175,7 +175,7 @@ int sys_acl_delete_def_file(const char *name)
|
||||
return acl_delete_def_file(name);
|
||||
}
|
||||
|
||||
int sys_acl_free_acl(SMB_ACL_T the_acl)
|
||||
int sys_acl_free_acl(SMB_ACL_T the_acl)
|
||||
{
|
||||
return acl_free(the_acl);
|
||||
}
|
||||
@@ -185,7 +185,7 @@ int sys_acl_free_acl(SMB_ACL_T the_acl)
|
||||
* The interface to DEC/Compaq Tru64 UNIX ACLs
|
||||
* is based on Draft 13 of the POSIX spec which is
|
||||
* slightly different from the Draft 16 interface.
|
||||
*
|
||||
*
|
||||
* Also, some of the permset manipulation functions
|
||||
* such as acl_clear_perm() and acl_add_perm() appear
|
||||
* to be broken on Tru64 so we have to manipulate
|
||||
@@ -310,7 +310,7 @@ int sys_acl_delete_def_file(const char *name)
|
||||
return acl_delete_def_file((char *)name);
|
||||
}
|
||||
|
||||
int sys_acl_free_acl(SMB_ACL_T the_acl)
|
||||
int sys_acl_free_acl(SMB_ACL_T the_acl)
|
||||
{
|
||||
return acl_free(the_acl);
|
||||
}
|
||||
@@ -457,7 +457,7 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
|
||||
break;
|
||||
}
|
||||
ndefault = count - naccess;
|
||||
|
||||
|
||||
/*
|
||||
* if the caller wants the default ACL we have to copy
|
||||
* the entries down to the start of the acl[] buffer
|
||||
@@ -517,7 +517,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
|
||||
if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
acl_d->count = naccess;
|
||||
|
||||
return acl_d;
|
||||
@@ -532,7 +532,7 @@ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *b
|
||||
|
||||
if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
|
||||
*u_g_id_p = entry->a_id;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -633,7 +633,7 @@ static int acl_sort(SMB_ACL_T acl_d)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int sys_acl_valid(SMB_ACL_T acl_d)
|
||||
{
|
||||
return acl_sort(acl_d);
|
||||
@@ -755,11 +755,11 @@ int sys_acl_delete_def_file(const char *path)
|
||||
ret = acl(path, SETACL, acl_d->count, acl_d->acl);
|
||||
|
||||
sys_acl_free_acl(acl_d);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sys_acl_free_acl(SMB_ACL_T acl_d)
|
||||
int sys_acl_free_acl(SMB_ACL_T acl_d)
|
||||
{
|
||||
SAFE_FREE(acl_d);
|
||||
return 0;
|
||||
@@ -895,10 +895,10 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
|
||||
int ndefault; /* # of default ACL entries */
|
||||
|
||||
if (hpux_acl_call_presence() == False) {
|
||||
/* Looks like we don't have the acl() system call on HPUX.
|
||||
/* Looks like we don't have the acl() system call on HPUX.
|
||||
* May be the system doesn't have the latest version of JFS.
|
||||
*/
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
|
||||
@@ -949,7 +949,7 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
|
||||
break;
|
||||
}
|
||||
ndefault = count - naccess;
|
||||
|
||||
|
||||
/*
|
||||
* if the caller wants the default ACL we have to copy
|
||||
* the entries down to the start of the acl[] buffer
|
||||
@@ -1109,9 +1109,9 @@ struct hpux_acl_types {
|
||||
* aclp - Array of ACL structures.
|
||||
* acl_type_count - Pointer to acl_types structure. Should already be
|
||||
* allocated.
|
||||
* Output:
|
||||
* Output:
|
||||
*
|
||||
* acl_type_count - This structure is filled up with counts of various
|
||||
* acl_type_count - This structure is filled up with counts of various
|
||||
* acl types.
|
||||
*/
|
||||
|
||||
@@ -1123,28 +1123,28 @@ static void hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_type
|
||||
|
||||
for (i = 0; i < acl_count; i++) {
|
||||
switch (aclp[i].a_type) {
|
||||
case USER:
|
||||
case USER:
|
||||
acl_type_count->n_user++;
|
||||
break;
|
||||
case USER_OBJ:
|
||||
case USER_OBJ:
|
||||
acl_type_count->n_user_obj++;
|
||||
break;
|
||||
case DEF_USER_OBJ:
|
||||
case DEF_USER_OBJ:
|
||||
acl_type_count->n_def_user_obj++;
|
||||
break;
|
||||
case GROUP:
|
||||
case GROUP:
|
||||
acl_type_count->n_group++;
|
||||
break;
|
||||
case GROUP_OBJ:
|
||||
case GROUP_OBJ:
|
||||
acl_type_count->n_group_obj++;
|
||||
break;
|
||||
case DEF_GROUP_OBJ:
|
||||
case DEF_GROUP_OBJ:
|
||||
acl_type_count->n_def_group_obj++;
|
||||
break;
|
||||
case OTHER_OBJ:
|
||||
case OTHER_OBJ:
|
||||
acl_type_count->n_other_obj++;
|
||||
break;
|
||||
case DEF_OTHER_OBJ:
|
||||
case DEF_OTHER_OBJ:
|
||||
acl_type_count->n_def_other_obj++;
|
||||
break;
|
||||
case CLASS_OBJ:
|
||||
@@ -1159,14 +1159,14 @@ static void hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_type
|
||||
case DEF_GROUP:
|
||||
acl_type_count->n_def_group++;
|
||||
break;
|
||||
default:
|
||||
default:
|
||||
acl_type_count->n_illegal_obj++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* swap_acl_entries: Swaps two ACL entries.
|
||||
/* swap_acl_entries: Swaps two ACL entries.
|
||||
*
|
||||
* Inputs: aclp0, aclp1 - ACL entries to be swapped.
|
||||
*/
|
||||
@@ -1189,25 +1189,25 @@ static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
|
||||
}
|
||||
|
||||
/* prohibited_duplicate_type
|
||||
* Identifies if given ACL type can have duplicate entries or
|
||||
* Identifies if given ACL type can have duplicate entries or
|
||||
* not.
|
||||
*
|
||||
* Inputs: acl_type - ACL Type.
|
||||
*
|
||||
* Outputs:
|
||||
* Outputs:
|
||||
*
|
||||
* Return..
|
||||
* Return..
|
||||
*
|
||||
* True - If the ACL type matches any of the prohibited types.
|
||||
* False - If the ACL type doesn't match any of the prohibited types.
|
||||
*/
|
||||
*/
|
||||
|
||||
static BOOL hpux_prohibited_duplicate_type(int acl_type)
|
||||
{
|
||||
switch (acl_type) {
|
||||
case USER:
|
||||
case GROUP:
|
||||
case DEF_USER:
|
||||
case DEF_USER:
|
||||
case DEF_GROUP:
|
||||
return True;
|
||||
default:
|
||||
@@ -1217,7 +1217,7 @@ static BOOL hpux_prohibited_duplicate_type(int acl_type)
|
||||
|
||||
/* get_needed_class_perm
|
||||
* Returns the permissions of a ACL structure only if the ACL
|
||||
* type matches one of the pre-determined types for computing
|
||||
* type matches one of the pre-determined types for computing
|
||||
* CLASS_OBJ permissions.
|
||||
*
|
||||
* Inputs: aclp - Pointer to ACL structure.
|
||||
@@ -1226,17 +1226,17 @@ static BOOL hpux_prohibited_duplicate_type(int acl_type)
|
||||
static int hpux_get_needed_class_perm(struct acl *aclp)
|
||||
{
|
||||
switch (aclp->a_type) {
|
||||
case USER:
|
||||
case GROUP_OBJ:
|
||||
case GROUP:
|
||||
case DEF_USER_OBJ:
|
||||
case USER:
|
||||
case GROUP_OBJ:
|
||||
case GROUP:
|
||||
case DEF_USER_OBJ:
|
||||
case DEF_USER:
|
||||
case DEF_GROUP_OBJ:
|
||||
case DEF_GROUP_OBJ:
|
||||
case DEF_GROUP:
|
||||
case DEF_CLASS_OBJ:
|
||||
case DEF_OTHER_OBJ:
|
||||
case DEF_OTHER_OBJ:
|
||||
return aclp->a_perm;
|
||||
default:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1267,15 +1267,15 @@ static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
|
||||
#if !defined(HAVE_HPUX_ACLSORT)
|
||||
/*
|
||||
* The aclsort() system call is available on the latest HPUX General
|
||||
* Patch Bundles. So for HPUX, we developed our version of acl_sort
|
||||
* function. Because, we don't want to update to a new
|
||||
* Patch Bundles. So for HPUX, we developed our version of acl_sort
|
||||
* function. Because, we don't want to update to a new
|
||||
* HPUX GR bundle just for aclsort() call.
|
||||
*/
|
||||
|
||||
struct hpux_acl_types acl_obj_count;
|
||||
int n_class_obj_perm = 0;
|
||||
int i, j;
|
||||
|
||||
|
||||
if (!acl_count) {
|
||||
DEBUG(10, ("Zero acl count passed. Returning Success\n"));
|
||||
return 0;
|
||||
@@ -1290,8 +1290,8 @@ static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
|
||||
|
||||
hpux_count_obj(acl_count, aclp, &acl_obj_count);
|
||||
|
||||
/* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
|
||||
* CLASS_OBJ and OTHER_OBJ
|
||||
/* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
|
||||
* CLASS_OBJ and OTHER_OBJ
|
||||
*/
|
||||
|
||||
if (acl_obj_count.n_user_obj != 1
|
||||
@@ -1313,15 +1313,15 @@ or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
|
||||
* structures.
|
||||
/* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
|
||||
* structures.
|
||||
*
|
||||
* Sorting crieteria - First sort by ACL type. If there are multiple entries of
|
||||
* same ACL type, sort by ACL id.
|
||||
*
|
||||
* I am using the trivial kind of sorting method here because, performance isn't
|
||||
* I am using the trivial kind of sorting method here because, performance isn't
|
||||
* really effected by the ACLs feature. More over there aren't going to be more
|
||||
* than 17 entries on HPUX.
|
||||
* than 17 entries on HPUX.
|
||||
*/
|
||||
|
||||
for (i = 0; i < acl_count; i++) {
|
||||
@@ -1390,7 +1390,7 @@ static int acl_sort(SMB_ACL_T acl_d)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int sys_acl_valid(SMB_ACL_T acl_d)
|
||||
{
|
||||
return acl_sort(acl_d);
|
||||
@@ -1405,11 +1405,11 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
|
||||
int ret;
|
||||
|
||||
if (hpux_acl_call_presence() == False) {
|
||||
/* Looks like we don't have the acl() system call on HPUX.
|
||||
/* Looks like we don't have the acl() system call on HPUX.
|
||||
* May be the system doesn't have the latest version of JFS.
|
||||
*/
|
||||
errno=ENOSYS;
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
|
||||
@@ -1538,11 +1538,11 @@ int sys_acl_delete_def_file(const char *path)
|
||||
ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
|
||||
|
||||
sys_acl_free_acl(acl_d);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sys_acl_free_acl(SMB_ACL_T acl_d)
|
||||
int sys_acl_free_acl(SMB_ACL_T acl_d)
|
||||
{
|
||||
free(acl_d);
|
||||
return 0;
|
||||
@@ -1723,7 +1723,7 @@ int sys_acl_delete_def_file(const char *name)
|
||||
return acl_delete_def_file(name);
|
||||
}
|
||||
|
||||
int sys_acl_free_acl(SMB_ACL_T acl_d)
|
||||
int sys_acl_free_acl(SMB_ACL_T acl_d)
|
||||
{
|
||||
if (acl_d->freeaclp) {
|
||||
acl_free(acl_d->aclp);
|
||||
@@ -1834,12 +1834,12 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
|
||||
}
|
||||
|
||||
/* Get the acl using statacl */
|
||||
|
||||
|
||||
DEBUG(10, ("Entering sys_acl_get_file\n"));
|
||||
DEBUG(10, ("path_p is %s\n", path_p));
|
||||
|
||||
file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
|
||||
|
||||
|
||||
if (file_acl == NULL) {
|
||||
errno=ENOMEM;
|
||||
DEBUG(0, ("Error in AIX sys_acl_get_file: %d\n", errno));
|
||||
@@ -1931,9 +1931,9 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
|
||||
* to be specified but, it's better than leaving it 0 */
|
||||
|
||||
acl_entry_link->entryp->ace_type = acl_entry->ace_type;
|
||||
|
||||
|
||||
acl_entry_link->entryp->ace_access = acl_entry->ace_access;
|
||||
|
||||
|
||||
memcpy(acl_entry_link->entryp->ace_id, idp, sizeof (struct ace_id));
|
||||
|
||||
/* The access in the acl entries must be left shifted by *
|
||||
@@ -1962,7 +1962,7 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
|
||||
|
||||
DEBUG(10, ("acl_entry = %d\n", acl_entry));
|
||||
DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
|
||||
|
||||
|
||||
acl_entry = acl_nxt(acl_entry);
|
||||
}
|
||||
} /* end of if enabled */
|
||||
@@ -2014,12 +2014,12 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
|
||||
new_acl_entry->ace_access = file_acl->o_access << 6;
|
||||
idp->id_type = SMB_ACL_OTHER;
|
||||
break;
|
||||
|
||||
|
||||
case 1:
|
||||
new_acl_entry->ace_access = file_acl->u_access << 6;
|
||||
idp->id_type = SMB_ACL_USER_OBJ;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
|
||||
@@ -2048,7 +2048,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
|
||||
int rc = 0;
|
||||
|
||||
/* Get the acl using fstatacl */
|
||||
|
||||
|
||||
DEBUG(10, ("Entering sys_acl_get_fd\n"));
|
||||
DEBUG(10, ("fd is %d\n", fd));
|
||||
file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
|
||||
@@ -2095,12 +2095,12 @@ SMB_ACL_T sys_acl_get_fd(int fd)
|
||||
|
||||
DEBUG(10, ("acl_entry is %d\n", acl_entry));
|
||||
DEBUG(10, ("acl_last(file_acl) id %d\n", acl_last(file_acl)));
|
||||
|
||||
|
||||
/* Check if the extended acl bit is on. *
|
||||
* If it isn't, do not show the *
|
||||
* contents of the acl since AIX intends *
|
||||
* the extended info to remain unused */
|
||||
|
||||
|
||||
if (file_acl->acl_mode & S_IXACL){
|
||||
/* while we are not pointing to the very end */
|
||||
while (acl_entry < acl_last(file_acl)) {
|
||||
@@ -2115,7 +2115,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
|
||||
}
|
||||
|
||||
idp = acl_entry->ace_id;
|
||||
|
||||
|
||||
/* Check if this is the first entry in the linked list. *
|
||||
* The first entry needs to keep prevp pointing to NULL *
|
||||
* and already has entryp allocated. */
|
||||
@@ -2177,7 +2177,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
|
||||
|
||||
DEBUG(10, ("acl_entry = %d\n", acl_entry));
|
||||
DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
|
||||
|
||||
|
||||
acl_entry = acl_nxt(acl_entry);
|
||||
}
|
||||
} /* end of if enabled */
|
||||
@@ -2210,43 +2210,43 @@ SMB_ACL_T sys_acl_get_fd(int fd)
|
||||
}
|
||||
|
||||
acl_entry_link->nextp = NULL;
|
||||
|
||||
|
||||
new_acl_entry = acl_entry_link->entryp;
|
||||
idp = new_acl_entry->ace_id;
|
||||
|
||||
|
||||
new_acl_entry->ace_len = sizeof (struct acl_entry);
|
||||
new_acl_entry->ace_type = ACC_PERMIT;
|
||||
idp->id_len = sizeof (struct ace_id);
|
||||
DEBUG(10, ("idp->id_len = %d\n", idp->id_len));
|
||||
memset(idp->id_data, 0, sizeof (uid_t));
|
||||
|
||||
|
||||
switch (i) {
|
||||
case 2:
|
||||
new_acl_entry->ace_access = file_acl->g_access << 6;
|
||||
idp->id_type = SMB_ACL_GROUP_OBJ;
|
||||
break;
|
||||
|
||||
|
||||
case 3:
|
||||
new_acl_entry->ace_access = file_acl->o_access << 6;
|
||||
idp->id_type = SMB_ACL_OTHER;
|
||||
break;
|
||||
|
||||
|
||||
case 1:
|
||||
new_acl_entry->ace_access = file_acl->u_access << 6;
|
||||
idp->id_type = SMB_ACL_USER_OBJ;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
acl_entry_link_head->count++;
|
||||
DEBUG(10, ("new_acl_entry->ace_access = %d\n", new_acl_entry->ace_access));
|
||||
}
|
||||
|
||||
acl_entry_link_head->count = 0;
|
||||
SAFE_FREE(file_acl);
|
||||
|
||||
|
||||
return acl_entry_link_head;
|
||||
}
|
||||
#endif
|
||||
@@ -2274,7 +2274,7 @@ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *b
|
||||
SMB_ACL_T sys_acl_init(int count)
|
||||
{
|
||||
struct acl_entry_link *theacl = NULL;
|
||||
|
||||
|
||||
if (count < 0) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
@@ -2383,9 +2383,9 @@ int sys_acl_valid(SMB_ACL_T theacl)
|
||||
}
|
||||
|
||||
DEBUG(10, ("user_obj=%d, group_obj=%d, other_obj=%d\n", user_obj, group_obj, other_obj));
|
||||
|
||||
|
||||
if (user_obj != 1 || group_obj != 1 || other_obj != 1)
|
||||
return -1;
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2404,7 +2404,7 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
|
||||
|
||||
DEBUG(10, ("Entering sys_acl_set_file\n"));
|
||||
DEBUG(10, ("File name is %s\n", name));
|
||||
|
||||
|
||||
/* AIX has no default ACL */
|
||||
if (acltype == SMB_ACL_TYPE_DEFAULT)
|
||||
return 0;
|
||||
@@ -2449,7 +2449,7 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
|
||||
errno = ENOMEM;
|
||||
DEBUG(0, ("Error in sys_acl_set_file is %d\n", errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(file_acl_temp, file_acl, file_acl->acl_len);
|
||||
SAFE_FREE(file_acl);
|
||||
@@ -2460,15 +2460,15 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
|
||||
file_acl->acl_len += sizeof (struct acl_entry);
|
||||
acl_entry->ace_len = acl_entry_link->entryp->ace_len;
|
||||
acl_entry->ace_access = acl_entry_link->entryp->ace_access;
|
||||
|
||||
|
||||
/* In order to use this, we'll need to wait until we can get denies */
|
||||
/* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
|
||||
acl_entry->ace_type = ACC_SPECIFY; */
|
||||
|
||||
acl_entry->ace_type = ACC_SPECIFY;
|
||||
|
||||
|
||||
ace_id = acl_entry->ace_id;
|
||||
|
||||
|
||||
ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
|
||||
DEBUG(10, ("The id type is %d\n", ace_id->id_type));
|
||||
ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
|
||||
@@ -2496,7 +2496,7 @@ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
|
||||
uint user_id;
|
||||
uint acl_length;
|
||||
uint rc;
|
||||
|
||||
|
||||
DEBUG(10, ("Entering sys_acl_set_fd\n"));
|
||||
acl_length = BUFSIZ;
|
||||
file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
|
||||
@@ -2508,7 +2508,7 @@ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
|
||||
}
|
||||
|
||||
memset(file_acl, 0, BUFSIZ);
|
||||
|
||||
|
||||
file_acl->acl_len = ACL_SIZ;
|
||||
file_acl->acl_mode = S_IXACL;
|
||||
|
||||
@@ -2550,22 +2550,22 @@ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
|
||||
file_acl->acl_len += sizeof (struct acl_entry);
|
||||
acl_entry->ace_len = acl_entry_link->entryp->ace_len;
|
||||
acl_entry->ace_access = acl_entry_link->entryp->ace_access;
|
||||
|
||||
|
||||
/* In order to use this, we'll need to wait until we can get denies */
|
||||
/* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
|
||||
acl_entry->ace_type = ACC_SPECIFY; */
|
||||
|
||||
|
||||
acl_entry->ace_type = ACC_SPECIFY;
|
||||
|
||||
|
||||
ace_id = acl_entry->ace_id;
|
||||
|
||||
|
||||
ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
|
||||
DEBUG(10, ("The id type is %d\n", ace_id->id_type));
|
||||
ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
|
||||
memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof (uid_t));
|
||||
memcpy(ace_id->id_data, &user_id, sizeof (uid_t));
|
||||
}
|
||||
|
||||
|
||||
rc = fchacl(fd, file_acl, file_acl->acl_len);
|
||||
DEBUG(10, ("errno is %d\n", errno));
|
||||
DEBUG(10, ("return code is %d\n", rc));
|
||||
@@ -2594,7 +2594,7 @@ int sys_acl_free_acl(SMB_ACL_T posix_acl)
|
||||
SAFE_FREE(acl_entry_link->prevp);
|
||||
SAFE_FREE(acl_entry_link->entryp);
|
||||
SAFE_FREE(acl_entry_link);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
* Version 2.2.x
|
||||
* Portable SMB ACL interface
|
||||
* Copyright (C) Jeremy Allison 2000
|
||||
* 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
|
||||
@@ -232,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
|
||||
|
||||
|
||||
+12
-3
@@ -2,7 +2,7 @@
|
||||
* Extended attribute support for rsync.
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc.
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
* Written by Jay Fenlason.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+4
-4
@@ -65,7 +65,7 @@ typedef enum {
|
||||
|
||||
struct enum_list {
|
||||
int value;
|
||||
char *name;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct parm_struct {
|
||||
@@ -73,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;
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ static item_list section_list = EMPTY_ITEM_LIST;
|
||||
static int iSectionIndex = -1;
|
||||
static BOOL bInGlobalSection = True;
|
||||
|
||||
static struct enum_list enum_syslog_facility[] = {
|
||||
static const struct enum_list enum_syslog_facility[] = {
|
||||
#ifdef LOG_AUTH
|
||||
{ LOG_AUTH, "auth" },
|
||||
#endif
|
||||
@@ -178,7 +178,7 @@ static char *expand_vars(const char *str)
|
||||
|
||||
for (t = buf, f = str; bufsize && *f; ) {
|
||||
if (*f == '%' && isUpper(f+1)) {
|
||||
char *percent = strchr(f+1, '%');
|
||||
const char *percent = strchr(f+1, '%');
|
||||
if (percent && percent - f < bufsize) {
|
||||
char *val;
|
||||
strlcpy(t, f+1, percent - f);
|
||||
|
||||
@@ -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-2021 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
|
||||
@@ -36,8 +36,6 @@ extern int protocol_version;
|
||||
extern int always_checksum;
|
||||
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;
|
||||
@@ -456,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,
|
||||
@@ -680,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';
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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-2021 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
|
||||
@@ -48,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;
|
||||
@@ -65,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;
|
||||
@@ -88,6 +89,8 @@ 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;
|
||||
@@ -103,7 +106,7 @@ 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;
|
||||
@@ -236,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()
|
||||
@@ -383,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];
|
||||
@@ -391,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 = ", ";
|
||||
}
|
||||
}
|
||||
@@ -477,7 +489,7 @@ static void show_malloc_stats(void)
|
||||
|
||||
#define PRINT_ALLOC_NUM(title, descr, num) \
|
||||
rprintf(FINFO, " %-11s%10" SIZE_T_FMT_MOD "d (" descr ")\n", \
|
||||
title ":", (SIZE_T_FMT_CAST)(num));
|
||||
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);
|
||||
@@ -607,11 +619,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
|
||||
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--;
|
||||
}
|
||||
}
|
||||
@@ -663,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,
|
||||
@@ -690,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, '/');
|
||||
@@ -815,7 +832,16 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
|
||||
dest_path = "/";
|
||||
|
||||
*cp = '\0';
|
||||
if (!change_dir(dest_path, CD_NORMAL)) {
|
||||
if (dry_run && mkpath_dest_arg && do_stat(dest_path, &st) < 0) {
|
||||
/* --mkpath would have created this parent dir, but a dry run did
|
||||
* not, so don't chdir into it; flag the destination as not yet
|
||||
* present (as the dir-creation path above does) so the generator
|
||||
* doesn't try to compare against the missing tree (#880). Only
|
||||
* the missing-parent case is touched, so an ordinary file-to-file
|
||||
* dry run still itemizes against an existing destination. */
|
||||
dry_run++;
|
||||
change_dir(dest_path, CD_SKIP_CHDIR);
|
||||
} else if (!change_dir(dest_path, CD_NORMAL)) {
|
||||
rsyserr(FERROR, errno, "change_dir#3 %s failed",
|
||||
full_fname(dest_path));
|
||||
exit_cleanup(RERR_FILESELECT);
|
||||
@@ -1079,6 +1105,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);
|
||||
@@ -1374,15 +1401,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.
|
||||
@@ -1398,10 +1416,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 */
|
||||
@@ -1434,6 +1448,8 @@ static int start_client(int argc, char *argv[])
|
||||
|
||||
if (argc > 1) {
|
||||
p = argv[--argc];
|
||||
if (!*p)
|
||||
p = dot_dir_or_error();
|
||||
remote_argv = argv + argc;
|
||||
} else {
|
||||
static char *dotarg[1] = { "." };
|
||||
@@ -1474,6 +1490,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 = ".";
|
||||
|
||||
@@ -1499,6 +1521,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++) {
|
||||
@@ -1522,6 +1546,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1552,6 +1577,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)) {
|
||||
@@ -1736,7 +1765,20 @@ int main(int argc,char *argv[])
|
||||
our_gid = MY_GID();
|
||||
am_root = our_uid == ROOT_UID;
|
||||
|
||||
unset_env_var("DISPLAY");
|
||||
// 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));
|
||||
|
||||
@@ -1807,7 +1849,7 @@ int main(int argc,char *argv[])
|
||||
if (am_server && protect_args) {
|
||||
char buf[MAXPATHLEN];
|
||||
protect_args = 2;
|
||||
read_args(STDIN_FILENO, NULL, buf, sizeof buf, 1, &argv, &argc, NULL);
|
||||
read_args(STDIN_FILENO, NULL, buf, sizeof buf, 1, 0, &argv, &argc, NULL);
|
||||
if (!parse_arguments(&argc, (const char ***) &argv)) {
|
||||
option_error();
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
|
||||
@@ -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",
|
||||
|
||||
+8
-1
@@ -8,6 +8,7 @@ fi
|
||||
inname="$1"
|
||||
srcdir=`dirname "$0"`
|
||||
flagfile="$srcdir/.md2man-works"
|
||||
force_flagfile="$srcdir/.md2man-force"
|
||||
|
||||
if [ ! -f "$flagfile" ]; then
|
||||
# We test our smallest manpage just to see if the python setup works.
|
||||
@@ -32,4 +33,10 @@ if [ ! -f "$flagfile" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
"$srcdir/md-convert" "$srcdir/$inname"
|
||||
if [ -f "$force_flagfile" ]; then
|
||||
opt='--force-link-text'
|
||||
else
|
||||
opt=''
|
||||
fi
|
||||
|
||||
"$srcdir/md-convert" $opt "$srcdir/$inname"
|
||||
+212
-21
@@ -32,11 +32,14 @@
|
||||
import os, sys, re, argparse, subprocess, time
|
||||
from html.parser import HTMLParser
|
||||
|
||||
CONSUMES_TXT = set('h1 h2 p li pre'.split())
|
||||
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>%s</title>
|
||||
<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 {
|
||||
@@ -46,6 +49,10 @@ body {
|
||||
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;
|
||||
@@ -106,9 +113,28 @@ 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)
|
||||
@@ -118,15 +144,15 @@ def main():
|
||||
|
||||
|
||||
def parse_md_file(mdfn):
|
||||
fi = re.match(r'^(?P<fn>(?P<srcdir>.+/)?(?P<name>(?P<prog>[^/]+?)(\.(?P<sect>\d+))?)\.md)$', 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 + ') man page'
|
||||
fi.title = fi.prog + '(' + fi.sect + ') manpage'
|
||||
else:
|
||||
fi.title = fi.prog
|
||||
fi.title = fi.prog + ' for rsync'
|
||||
|
||||
if fi.want_manpage:
|
||||
if not env_subs:
|
||||
@@ -165,6 +191,8 @@ def parse_md_file(mdfn):
|
||||
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)
|
||||
@@ -187,6 +215,7 @@ def find_man_substitutions():
|
||||
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:
|
||||
@@ -198,24 +227,29 @@ def find_man_substitutions():
|
||||
|
||||
with open(srcdir + 'version.h', 'r', encoding='utf-8') as fh:
|
||||
txt = fh.read()
|
||||
m = re.search(r'"(.+?)"', txt)
|
||||
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 = re.match(r'^(\w+)=(.+)', line)
|
||||
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 re.search(r'\$\{', val):
|
||||
val = re.sub(r'\$\{(\w+)\}', lambda m: env_subs[m.group(1)], val)
|
||||
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.localtime(mtime))
|
||||
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):
|
||||
@@ -226,6 +260,8 @@ 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",
|
||||
@@ -234,10 +270,21 @@ class TransformHtml(HTMLParser):
|
||||
dt_from = None,
|
||||
in_pre = False,
|
||||
in_code = False,
|
||||
html_out = [ HTML_START % fi.title ],
|
||||
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:
|
||||
@@ -260,6 +307,27 @@ class TransformHtml(HTMLParser):
|
||||
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
|
||||
@@ -300,7 +368,7 @@ class TransformHtml(HTMLParser):
|
||||
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 man page
|
||||
tag = 'u' # Change it into underline to be more like the manpage
|
||||
st.txt += UNDR_FONT[0]
|
||||
elif tag == 'ol':
|
||||
start = 1
|
||||
@@ -328,6 +396,32 @@ class TransformHtml(HTMLParser):
|
||||
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
|
||||
|
||||
@@ -336,6 +430,8 @@ class TransformHtml(HTMLParser):
|
||||
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 = ''
|
||||
@@ -343,13 +439,27 @@ class TransformHtml(HTMLParser):
|
||||
txt = None
|
||||
add_to_txt = None
|
||||
if tag == 'h1':
|
||||
st.man_out.append(st.p_macro + '.SH "' + manify(txt) + '"\n')
|
||||
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")
|
||||
@@ -373,7 +483,7 @@ class TransformHtml(HTMLParser):
|
||||
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 man page
|
||||
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':
|
||||
@@ -385,6 +495,30 @@ class TransformHtml(HTMLParser):
|
||||
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:
|
||||
@@ -403,21 +537,57 @@ class TransformHtml(HTMLParser):
|
||||
|
||||
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 = 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)
|
||||
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 = re.sub(r'\s', NBR_SPACE[0], txt)
|
||||
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
|
||||
@@ -431,13 +601,28 @@ class TransformHtml(HTMLParser):
|
||||
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 re.sub(r"^(['.])", r'\&\1', txt.replace('\\', '\\\\')
|
||||
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]), flags=re.M)
|
||||
.replace(UNDR_FONT[0], UNDR_FONT[1]))
|
||||
|
||||
|
||||
def htmlify(txt):
|
||||
@@ -446,6 +631,8 @@ def htmlify(txt):
|
||||
|
||||
def warn(*msg):
|
||||
print(*msg, file=sys.stderr)
|
||||
global warning_count
|
||||
warning_count += 1
|
||||
|
||||
|
||||
def die(*msg):
|
||||
@@ -454,11 +641,13 @@ def die(*msg):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Output html and (optionally) nroff for markdown pages.", add_help=False)
|
||||
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", nargs='+', help="The source .md files to convert.")
|
||||
parser.add_argument("mdfiles", metavar='FILE.md', nargs='+', help="One or more .md files to convert.")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
@@ -474,3 +663,5 @@ if __name__ == '__main__':
|
||||
gfm_parser = None
|
||||
|
||||
main()
|
||||
if warning_count:
|
||||
sys.exit(1)
|
||||
@@ -1,14 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
srcdir=`dirname $0`
|
||||
gitver=`git describe --abbrev=8 2>/dev/null`
|
||||
|
||||
if [ ! -f git-version.h ]; then
|
||||
touch git-version.h
|
||||
fi
|
||||
|
||||
case "$gitver" in
|
||||
*.*)
|
||||
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"
|
||||
@@ -16,5 +18,5 @@ case "$gitver" in
|
||||
else
|
||||
rm git-version.h.new
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
@@ -1,4 +1,4 @@
|
||||
TARGETS := all install install-ssl-daemon install-all install-strip conf gen gensend reconfigure restatus \
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -6,7 +6,7 @@
|
||||
import re, argparse
|
||||
|
||||
short_no_arg = { }
|
||||
short_with_num = { '@': 1 };
|
||||
short_with_num = { '@': 1 }
|
||||
long_opts = { # These include some extra long-args that BackupPC uses:
|
||||
'block-size': 1,
|
||||
'daemon': -1,
|
||||
@@ -27,6 +27,7 @@ long_opts = { # These include some extra long-args that BackupPC uses:
|
||||
'recursive': 0,
|
||||
'stderr': 1,
|
||||
'times': 0,
|
||||
'copy-devices': -1,
|
||||
'write-devices': -1,
|
||||
}
|
||||
|
||||
@@ -57,11 +58,13 @@ def main():
|
||||
continue
|
||||
|
||||
if last_long_opt:
|
||||
m = re.search(r'args\[ac\+\+\] = ([^["\s]+);', line)
|
||||
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:
|
||||
@@ -73,7 +76,9 @@ def main():
|
||||
if not m:
|
||||
m = re.search(r'args\[ac\+\+\] = "--([^"=]+)=', line)
|
||||
if not m:
|
||||
m = re.search(r'fmt = .*: "--([^"=]+)=', line)
|
||||
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
|
||||
@@ -81,7 +86,7 @@ def main():
|
||||
long_opts['files-from'] = 3
|
||||
|
||||
txt = """\
|
||||
### START of options data produced by the cull_options script. ###
|
||||
### START of options data produced by the cull-options script. ###
|
||||
|
||||
# To disable a short-named option, add its letter to this string:
|
||||
"""
|
||||
@@ -119,7 +124,7 @@ def main():
|
||||
print("}")
|
||||
else:
|
||||
print(");")
|
||||
print("\n### END of options data produced by the cull_options script. ###")
|
||||
print("\n### END of options data produced by the cull-options script. ###")
|
||||
|
||||
|
||||
def str_assign(name, val, comment=None):
|
||||
@@ -1,9 +1,9 @@
|
||||
Summary: A fast, versatile, remote (and local) file-copying tool
|
||||
Name: rsync
|
||||
Version: 3.2.4
|
||||
%define fullversion %{version}pre1
|
||||
Release: 0.1.pre1
|
||||
%define srcdir src-previews
|
||||
Version: 3.4.4
|
||||
%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
|
||||
* Sun Jan 02 2022 Wayne Davison <wayne@opencoder.net>
|
||||
Released 3.2.4pre1.
|
||||
|
||||
* 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.
|
||||
* Mon Jun 08 2026 Rsync Project <rsync.project@gmail.com>
|
||||
Released 3.4.4.
|
||||
@@ -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,244 +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 = [
|
||||
'./prepare-source'.split(),
|
||||
'cd build && if test -f config.status ; then ./config.status ; else ../configure ; fi',
|
||||
'make -C build 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)
|
||||
|
||||
master_commit = latest_git_hash(args.base_branch)
|
||||
|
||||
if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
|
||||
die('You must setup an auto-build-save dir to use this script.')
|
||||
|
||||
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 = int(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 >= int(time.time()):
|
||||
time.sleep(1)
|
||||
cmd_chk(['git', 'checkout', starting_branch])
|
||||
cmd_chk(['packaging/prep-auto-dir'], discard='output')
|
||||
|
||||
|
||||
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 >= int(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
|
||||
skip_shell = False
|
||||
if not ok or args.cmd or args.make or args.shell:
|
||||
cmd_chk(['packaging/prep-auto-dir'], discard='output')
|
||||
if not ok:
|
||||
print(f'"git merge {based_on}" incomplete -- please fix.')
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
if not args.make and not args.cmd:
|
||||
skip_shell = True
|
||||
if args.make:
|
||||
if cmd_run(['packaging/smart-make']).returncode != 0:
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
if not args.cmd:
|
||||
skip_shell = True
|
||||
if args.cmd:
|
||||
if cmd_run(args.cmd).returncode != 0:
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
skip_shell = True
|
||||
if args.shell and not skip_shell:
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
|
||||
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 = int(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()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def run_a_shell(parent, patch):
|
||||
m = re.search(r'([^/]+)$', parent)
|
||||
parent_dir = m[1]
|
||||
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 False
|
||||
continue
|
||||
cur_branch, is_clean, status_txt = check_git_status(0)
|
||||
if is_clean:
|
||||
break
|
||||
print(status_txt, end='')
|
||||
|
||||
cmd_run('rm -f build/*.o build/*/*.o')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
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('--make', '-m', action='store_true', help="Run the smart-make script in every patch branch.")
|
||||
parser.add_argument('--cmd', '-c', help="Run a command in every patch 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
|
||||
+3
-14
@@ -32,7 +32,7 @@ def _tweak_opts(cmd, opts, **maybe_set_args):
|
||||
opts = opts.copy()
|
||||
_maybe_set(opts, **maybe_set_args)
|
||||
|
||||
if type(cmd) == str:
|
||||
if isinstance(cmd, str):
|
||||
_maybe_set(opts, shell=True)
|
||||
|
||||
want_raw = opts.pop('raw', False)
|
||||
@@ -170,18 +170,7 @@ 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:
|
||||
ct = cmd_txt(['fgrep', 'make gensend', hook], discard='output')
|
||||
if ct.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.
|
||||
# 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')
|
||||
|
||||
@@ -189,7 +178,7 @@ def get_gen_files(want_dir_plus_list=False):
|
||||
|
||||
auto_dir = os.path.join('auto-build-save', cmd_txt('git rev-parse --abbrev-ref HEAD').out.strip().replace('/', '%'))
|
||||
|
||||
with open('Makefile.in', 'r', encoding='utf-8') as fh:
|
||||
with open(auto_dir + '/Makefile', 'r', encoding='utf-8') as fh:
|
||||
for line in fh:
|
||||
if not gen_files:
|
||||
chk = re.sub(r'^GENFILES=', '', line)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
cat >/dev/null # Just discard stdin data
|
||||
|
||||
if [[ -f /proc/$PPID/cmdline ]]; then
|
||||
while read -d $'\0' arg ; do
|
||||
if [[ "$arg" == '--tags' ]] ; then
|
||||
exit 0
|
||||
fi
|
||||
done </proc/$PPID/cmdline
|
||||
fi
|
||||
|
||||
branch=`git rev-parse --abbrev-ref HEAD`
|
||||
if [[ "$branch" = master && "$*" == *github* ]]; then
|
||||
make gensend
|
||||
fi
|
||||
@@ -1,395 +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():
|
||||
if not os.path.isfile('packaging/release-rsync'):
|
||||
die('You must run this script from the top of your rsync checkout.')
|
||||
|
||||
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)
|
||||
|
||||
if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
|
||||
die('You must setup an auto-build-save dir to use this script.');
|
||||
|
||||
auto_dir, gen_files = get_gen_files(True)
|
||||
gen_pathnames = [ os.path.join(auto_dir, fn) for fn in gen_files ]
|
||||
|
||||
dash_line = '=' * 74
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
== This will release a new version of rsync onto an unsuspecting world. ==
|
||||
{dash_line}
|
||||
""")
|
||||
|
||||
with open('build/rsync.1') as fh:
|
||||
for line in fh:
|
||||
if line.startswith(r'.\" prefix='):
|
||||
doc_prefix = line.split('=')[1].strip()
|
||||
if doc_prefix != '/usr':
|
||||
warn(f"*** The documentation was built with prefix {doc_prefix} instead of /usr ***")
|
||||
die("*** Read the md2man script for a way to override this. ***")
|
||||
break
|
||||
if line.startswith('.P'):
|
||||
die("Failed to find the prefix comment at the start of the rsync.1 manpage.")
|
||||
|
||||
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')
|
||||
|
||||
curversion = get_rsync_version()
|
||||
|
||||
# All version values are strings!
|
||||
lastversion, last_protocol_version, pdate = get_NEWS_version_info()
|
||||
protocol_version, subprotocol_version = get_protocol_versions()
|
||||
|
||||
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')
|
||||
|
||||
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]).out
|
||||
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 curversion.endswith('dev'):
|
||||
lastversion = curversion
|
||||
|
||||
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)
|
||||
proto_changed = protocol_version != last_protocol_version
|
||||
if proto_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
|
||||
else:
|
||||
proto_change_date = ' ' * 11
|
||||
|
||||
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 version.h 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 = 'version.h 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 fn == 'version.h':
|
||||
txt = f'#define RSYNC_VERSION "{version}"\n'
|
||||
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 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 define in {fn}")
|
||||
elif fn == 'NEWS.md':
|
||||
efv = re.escape(finalversion)
|
||||
x_re = re.compile(r'^<.+>\s+# 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'<a name="{finalversion}"></a>\n\n# 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 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 = os.path.join(dest, srcdir, srctar_name)
|
||||
pattar_file = os.path.join(dest, srcdir, pattar_name)
|
||||
diff_file = os.path.join(dest, srcdiffdir, diff_name)
|
||||
lasttar_file = os.path.join(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
|
||||
run patch-update with the --make option (which opens a shell on error)
|
||||
""")
|
||||
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 gen')
|
||||
|
||||
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'\nRunning smart-make on all "patch/{args.master_branch}/*" branches ...')
|
||||
cmd_run(f"packaging/patch-update --branch={args.master_branch} --skip-check --make")
|
||||
|
||||
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').out
|
||||
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').out
|
||||
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 = [ os.path.join(rsync_lastver, fn) for fn 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_pathnames, '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())
|
||||
|
||||
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 INSTALL.md'.split()
|
||||
html_files = [ fn for fn in gen_pathnames if fn.endswith('.html') ]
|
||||
cmd_chk(['rsync', '-a', *md_files, *html_files, dest])
|
||||
cmd_chk(["./md-convert"] + [ 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
|
||||
Executable
+703
@@ -0,0 +1,703 @@
|
||||
#!/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/ git checkout of rsync-web (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')
|
||||
|
||||
# Local rsync-web checkout (sibling of rsync-git) is the source-of-truth for
|
||||
# the git-tracked html content. The maintainer pulls/commits/pushes there;
|
||||
# step-1-fetch just snapshots it into HTML_DIR for the release flow.
|
||||
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)
|
||||
# The .filt file lives in the ftp dir on the server; mirror down using the
|
||||
# transmitted filter, falling back to no filter on the very first pull.
|
||||
filt = os.path.join(FTP_DIR, '.filt')
|
||||
if os.path.exists(filt):
|
||||
opts = ['-aivOHP', f'-f:_{filt}']
|
||||
else:
|
||||
opts = ['-aivOHP']
|
||||
cmd_chk(['rsync', *opts, 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. Clone the rsync-web repo there first.")
|
||||
if not os.path.isdir(os.path.join(HTML_SRC, '.git')):
|
||||
die(f"{HTML_SRC} exists but is not a git checkout.")
|
||||
print(f"(Make sure {HTML_SRC} is up to date — this script does not 'git pull' for you.)")
|
||||
os.makedirs(HTML_DIR, exist_ok=True)
|
||||
cmd_chk(['rsync', '-aiv', '--exclude=/.git',
|
||||
f'{HTML_SRC}/', f'{HTML_DIR}/'])
|
||||
|
||||
# Then mirror non-git html content from the server (mirroring samba-rsync's
|
||||
# behavior: skip files that the html git already provides).
|
||||
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}, run
|
||||
packaging/send-news (when convenient), 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
|
||||
Executable
+124
@@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
# This script makes it easy to update the ftp & html directories on the samba.org server.
|
||||
# It expects the 2 *_DEST directories to contain updated files that need to be sent to
|
||||
# the remote server. If these directories don't exist yet, they will be copied from the
|
||||
# remote server (while also making the html dir a git checkout).
|
||||
|
||||
FTP_SRC="$HOME/samba-rsync-ftp"
|
||||
HTML_SRC="$HOME/samba-rsync-html"
|
||||
|
||||
FTP_DEST="/home/ftp/pub/rsync"
|
||||
HTML_DEST="/home/httpd/html/rsync"
|
||||
|
||||
HTML_GIT='git.samba.org:/data/git/rsync-web.git'
|
||||
|
||||
export RSYNC_PARTIAL_DIR=''
|
||||
|
||||
case "$RSYNC_SAMBA_HOST" in
|
||||
*.samba.org) ;;
|
||||
*)
|
||||
echo "You must set RSYNC_SAMBA_HOST in your environment to the samba hostname to use." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
MODE=''
|
||||
REVERSE=''
|
||||
while (( $# )); do
|
||||
case "$1" in
|
||||
-R|--reverse) REVERSE=yes ;;
|
||||
f|ftp) MODE=ftp ;;
|
||||
h|html) MODE=html ;;
|
||||
-h|--help)
|
||||
echo "Usage: [-R] [f|ftp|h|html]"
|
||||
echo "-R --reverse Copy the files from the server to the local host."
|
||||
echo " The default is to update the remote files."
|
||||
echo "-h --help Output this help message."
|
||||
echo " "
|
||||
echo "The script will prompt if ftp or html is not specified on the command line."
|
||||
echo "Only one category can be copied at a time. When pulling html files, a git"
|
||||
echo "checkout will be either created or updated prior to the rsync copy."
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
while [ ! "$MODE" ]; do
|
||||
if [ "$REVERSE" = yes ]; then
|
||||
DIRECTION=FROM
|
||||
else
|
||||
DIRECTION=TO
|
||||
fi
|
||||
echo -n "Copy which files $DIRECTION the server? ftp or html? "
|
||||
read ans
|
||||
case "$ans" in
|
||||
f*) MODE=ftp ;;
|
||||
h*) MODE=html ;;
|
||||
'') exit 1 ;;
|
||||
*) echo "You must answer f or h to copy the ftp or html data." ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$MODE" = ftp ]; then
|
||||
SRC_DIR="$FTP_SRC"
|
||||
DEST_DIR="$FTP_DEST"
|
||||
FILT=".filt"
|
||||
else
|
||||
SRC_DIR="$HTML_SRC"
|
||||
DEST_DIR="$HTML_DEST"
|
||||
FILT="filt"
|
||||
fi
|
||||
|
||||
function do_rsync {
|
||||
rsync --dry-run "${@}" | grep -v 'is uptodate$'
|
||||
echo ''
|
||||
echo -n "Run without --dry-run? [n] "
|
||||
read ans
|
||||
case "$ans" in
|
||||
y*) rsync "${@}" | grep -v 'is uptodate$' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ -d "$SRC_DIR" ]; then
|
||||
REVERSE_RSYNC=do_rsync
|
||||
else
|
||||
echo "The directory $SRC_DIR does not exist yet."
|
||||
echo -n "Do you want to create it? [n] "
|
||||
read ans
|
||||
case "$ans" in
|
||||
y*) ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
REVERSE=yes
|
||||
REVERSE_RSYNC=rsync
|
||||
fi
|
||||
|
||||
if [ "$REVERSE" = yes ]; then
|
||||
OPTS='-aivOHP'
|
||||
TMP_FILT="$SRC_DIR/tmp-filt"
|
||||
echo "Copying files from $RSYNC_SAMBA_HOST to $SRC_DIR ..."
|
||||
if [ "$MODE" = html ]; then
|
||||
if [ $REVERSE_RSYNC = rsync ]; then
|
||||
git clone "$HTML_GIT" "$SRC_DIR" || exit 1
|
||||
else
|
||||
cd "$SRC_DIR" || exit 1
|
||||
git pull || exit 1
|
||||
fi
|
||||
sed -n -e 's/[-P]/H/p' "$SRC_DIR/$FILT" >"$TMP_FILT"
|
||||
OPTS="${OPTS}f._$TMP_FILT"
|
||||
else
|
||||
OPTS="${OPTS}f:_$FILT"
|
||||
fi
|
||||
$REVERSE_RSYNC "$OPTS" "$RSYNC_SAMBA_HOST:$DEST_DIR/" "$SRC_DIR/"
|
||||
rm -f "$TMP_FILT"
|
||||
exit
|
||||
fi
|
||||
|
||||
cd "$SRC_DIR" || exit 1
|
||||
echo "Copying files from $SRC_DIR to $RSYNC_SAMBA_HOST ..."
|
||||
do_rsync -aivOHP --chown=:rsync --del -f._$FILT . "$RSYNC_SAMBA_HOST:$DEST_DIR/"
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# This script expects the ~/src/rsync directory to contain the rsync
|
||||
# source that has been updated. It also expects the auto-build-save
|
||||
# directory to have been created prior to the running of configure so
|
||||
# that each branch has its own build directory underneath. This supports
|
||||
# the maintainer workflow for the rsync-patches files maintenace.
|
||||
|
||||
FTP_SRC="$HOME/samba-rsync-ftp"
|
||||
FTP_DEST="/home/ftp/pub/rsync"
|
||||
MD_FILES="README.md INSTALL.md NEWS.md"
|
||||
|
||||
case "$RSYNC_SAMBA_HOST" in
|
||||
*.samba.org) ;;
|
||||
*)
|
||||
echo "You must set RSYNC_SAMBA_HOST in your environment to the samba hostname to use." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -d "$FTP_SRC" ]; then
|
||||
packaging/samba-rsync ftp # Ask to initialize the local ftp dir
|
||||
fi
|
||||
|
||||
cd ~/src/rsync
|
||||
|
||||
make man
|
||||
./md-convert --dest="$FTP_SRC" $MD_FILES
|
||||
rsync -aiic $MD_FILES auto-build-save/master/*.?.html "$FTP_SRC"
|
||||
|
||||
cd "$FTP_SRC"
|
||||
|
||||
rsync -aiic README.* INSTALL.* NEWS.* *.?.html "$RSYNC_SAMBA_HOST:$FTP_DEST/"
|
||||
@@ -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:
|
||||
#
|
||||
|
||||
+35
-9
@@ -6,9 +6,10 @@
|
||||
|
||||
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():
|
||||
@@ -68,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
|
||||
|
||||
|
||||
+1
-55
@@ -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
@@ -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 */
|
||||
+957
-526
File diff suppressed because it is too large.
Load diff
+245
-228
@@ -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
|
||||
|
||||
+442
-116
@@ -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;
|
||||
}
|
||||
+418
-356
File diff suppressed because it is too large.
Load diff
+194
@@ -0,0 +1,194 @@
|
||||
#include "system.h"
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_LANGINFO_H
|
||||
#include <langinfo.h>
|
||||
#endif
|
||||
#include "poptint.h"
|
||||
|
||||
/* Any pair of 32 bit hashes can be used. lookup3.c generates pairs, will do. */
|
||||
#define _JLU3_jlu32lpair 1
|
||||
#define jlu32lpair poptJlu32lpair
|
||||
#include "lookup3.c"
|
||||
|
||||
const char *
|
||||
POPT_prev_char (const char *str)
|
||||
{
|
||||
const char *p = str;
|
||||
|
||||
while (1) {
|
||||
p--;
|
||||
if (((unsigned)*p & 0xc0) != (unsigned)0x80)
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
POPT_next_char (const char *str)
|
||||
{
|
||||
const char *p = str;
|
||||
|
||||
while (*p != '\0') {
|
||||
p++;
|
||||
if (((unsigned)*p & 0xc0) != (unsigned)0x80)
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#if !defined(POPT_fprintf) /* XXX lose all the goop ... */
|
||||
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
|
||||
/*
|
||||
* Rebind a "UTF-8" codeset for popt's internal use.
|
||||
*/
|
||||
char *
|
||||
POPT_dgettext(const char * dom, const char * str)
|
||||
{
|
||||
char * codeset = NULL;
|
||||
char * retval = NULL;
|
||||
|
||||
if (!dom)
|
||||
dom = textdomain(NULL);
|
||||
codeset = bind_textdomain_codeset(dom, NULL);
|
||||
bind_textdomain_codeset(dom, "UTF-8");
|
||||
retval = dgettext(dom, str);
|
||||
bind_textdomain_codeset(dom, codeset);
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ICONV
|
||||
/**
|
||||
* Return malloc'd string converted from UTF-8 to current locale.
|
||||
* @param istr input string (UTF-8 encoding assumed)
|
||||
* @return localized string
|
||||
*/
|
||||
static char *
|
||||
strdup_locale_from_utf8 (char * istr)
|
||||
{
|
||||
char * codeset = NULL;
|
||||
char * ostr = NULL;
|
||||
iconv_t cd;
|
||||
|
||||
if (istr == NULL)
|
||||
return NULL;
|
||||
|
||||
#ifdef HAVE_LANGINFO_H
|
||||
codeset = nl_langinfo ((nl_item)CODESET);
|
||||
#endif
|
||||
|
||||
if (codeset != NULL && strcmp(codeset, "UTF-8") != 0
|
||||
&& (cd = iconv_open(codeset, "UTF-8")) != (iconv_t)-1)
|
||||
{
|
||||
char * shift_pin = NULL;
|
||||
size_t db = strlen(istr);
|
||||
char * dstr = malloc((db + 1) * sizeof(*dstr));
|
||||
char * dstr_tmp;
|
||||
char * pin = istr;
|
||||
char * pout = dstr;
|
||||
size_t ib = db;
|
||||
size_t ob = db;
|
||||
size_t err;
|
||||
|
||||
if (dstr == NULL) {
|
||||
(void) iconv_close(cd);
|
||||
return NULL;
|
||||
}
|
||||
err = iconv(cd, NULL, NULL, NULL, NULL);
|
||||
while (1) {
|
||||
*pout = '\0';
|
||||
err = iconv(cd, &pin, &ib, &pout, &ob);
|
||||
if (err != (size_t)-1) {
|
||||
if (shift_pin == NULL) {
|
||||
shift_pin = pin;
|
||||
pin = NULL;
|
||||
ib = 0;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
switch (errno) {
|
||||
case E2BIG:
|
||||
{ size_t used = (size_t)(pout - dstr);
|
||||
db *= 2;
|
||||
dstr_tmp = realloc(dstr, (db + 1) * sizeof(*dstr));
|
||||
if (dstr_tmp == NULL) {
|
||||
free(dstr);
|
||||
(void) iconv_close(cd);
|
||||
return NULL;
|
||||
}
|
||||
dstr = dstr_tmp;
|
||||
pout = dstr + used;
|
||||
ob = db - used;
|
||||
continue;
|
||||
} break;
|
||||
case EINVAL:
|
||||
case EILSEQ:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
(void) iconv_close(cd);
|
||||
*pout = '\0';
|
||||
ostr = xstrdup(dstr);
|
||||
free(dstr);
|
||||
} else
|
||||
ostr = xstrdup(istr);
|
||||
|
||||
return ostr;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
POPT_fprintf (FILE * stream, const char * format, ...)
|
||||
{
|
||||
char * b = NULL, * ob = NULL;
|
||||
int rc;
|
||||
va_list ap;
|
||||
|
||||
#if defined(HAVE_VASPRINTF)
|
||||
va_start(ap, format);
|
||||
if ((rc = vasprintf(&b, format, ap)) < 0)
|
||||
b = NULL;
|
||||
va_end(ap);
|
||||
#else
|
||||
size_t nb = (size_t)1;
|
||||
|
||||
/* HACK: add +1 to the realloc no. of bytes "just in case". */
|
||||
/* XXX Likely unneeded, the issues wrto vsnprintf(3) return b0rkage have
|
||||
* to do with whether the final '\0' is counted (or not). The code
|
||||
* below already adds +1 for the (possibly already counted) trailing NUL.
|
||||
*/
|
||||
while ((b = realloc(b, nb+1)) != NULL) {
|
||||
va_start(ap, format);
|
||||
rc = vsnprintf(b, nb, format, ap);
|
||||
va_end(ap);
|
||||
if (rc > -1) { /* glibc 2.1 */
|
||||
if ((size_t)rc < nb)
|
||||
break;
|
||||
nb = (size_t)(rc + 1); /* precise buffer length known */
|
||||
} else /* glibc 2.0 */
|
||||
nb += (nb < (size_t)100 ? (size_t)100 : nb);
|
||||
ob = b;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = 0;
|
||||
if (b != NULL) {
|
||||
#ifdef HAVE_ICONV
|
||||
ob = strdup_locale_from_utf8(b);
|
||||
if (ob != NULL) {
|
||||
rc = fprintf(stream, "%s", ob);
|
||||
free(ob);
|
||||
} else
|
||||
#endif
|
||||
rc = fprintf(stream, "%s", b);
|
||||
free (b);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* !defined(POPT_fprintf) */
|
||||
+77
-41
@@ -1,5 +1,5 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/poptint.h
|
||||
* @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -9,108 +9,145 @@
|
||||
#ifndef H_POPTINT
|
||||
#define H_POPTINT
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
|
||||
* @param p memory to free
|
||||
* @retval NULL always
|
||||
*/
|
||||
/*@unused@*/ static inline /*@null@*/ void *
|
||||
_free(/*@only@*/ /*@null@*/ const void * p)
|
||||
/*@modifies p @*/
|
||||
static inline void *
|
||||
_free(const void * p)
|
||||
{
|
||||
if (p != NULL) free((void *)p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
isSpace(const char *ptr)
|
||||
{
|
||||
return isspace(*(unsigned char *)ptr);
|
||||
}
|
||||
|
||||
/* Bit mask macros. */
|
||||
/*@-exporttype -redef @*/
|
||||
typedef unsigned int __pbm_bits;
|
||||
/*@=exporttype =redef @*/
|
||||
#define __PBM_NBITS (8 * sizeof (__pbm_bits))
|
||||
#define __PBM_IX(d) ((d) / __PBM_NBITS)
|
||||
#define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
|
||||
/*@-exporttype -redef @*/
|
||||
typedef struct {
|
||||
__pbm_bits bits[1];
|
||||
} pbm_set;
|
||||
/*@=exporttype =redef @*/
|
||||
#define __PBM_BITS(set) ((set)->bits)
|
||||
|
||||
#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
|
||||
#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(pbm_set))
|
||||
#define PBM_FREE(s) _free(s);
|
||||
#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
|
||||
#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
|
||||
#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
|
||||
|
||||
extern void poptJlu32lpair(const void *key, size_t size,
|
||||
uint32_t *pc, uint32_t *pb);
|
||||
|
||||
/** \ingroup popt
|
||||
* Typedef's for string and array of strings.
|
||||
*/
|
||||
typedef const char * poptString;
|
||||
typedef poptString * poptArgv;
|
||||
|
||||
/** \ingroup popt
|
||||
* A union to simplify opt->arg access without casting.
|
||||
*/
|
||||
typedef union poptArg_u {
|
||||
void * ptr;
|
||||
int * intp;
|
||||
short * shortp;
|
||||
long * longp;
|
||||
long long * longlongp;
|
||||
float * floatp;
|
||||
double * doublep;
|
||||
const char ** argv;
|
||||
poptCallbackType cb;
|
||||
poptOption opt;
|
||||
} poptArg;
|
||||
|
||||
extern unsigned int _poptArgMask;
|
||||
extern unsigned int _poptGroupMask;
|
||||
|
||||
#define poptArgType(_opt) ((_opt)->argInfo & _poptArgMask)
|
||||
#define poptGroup(_opt) ((_opt)->argInfo & _poptGroupMask)
|
||||
|
||||
#define F_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_ARGFLAG_##_FLAG)
|
||||
#define LF_ISSET(_FLAG) (argInfo & POPT_ARGFLAG_##_FLAG)
|
||||
#define CBF_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_CBFLAG_##_FLAG)
|
||||
|
||||
/* XXX sick hack to preserve pretense of a popt-1.x ABI. */
|
||||
#define poptSubstituteHelpI18N(opt) \
|
||||
{ if ((opt) == poptHelpOptions) (opt) = poptHelpOptionsI18N; }
|
||||
|
||||
struct optionStackEntry {
|
||||
int argc;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char ** argv;
|
||||
/*@only@*/ /*@null@*/
|
||||
poptArgv argv;
|
||||
pbm_set * argb;
|
||||
int next;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char * nextArg;
|
||||
/*@observer@*/ /*@null@*/
|
||||
char * nextArg;
|
||||
const char * nextCharArg;
|
||||
/*@dependent@*/ /*@null@*/
|
||||
poptItem currAlias;
|
||||
int stuffed;
|
||||
};
|
||||
|
||||
struct poptContext_s {
|
||||
struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
|
||||
/*@dependent@*/
|
||||
struct optionStackEntry * os;
|
||||
/*@owned@*/ /*@null@*/
|
||||
const char ** leftovers;
|
||||
poptArgv leftovers;
|
||||
int numLeftovers;
|
||||
int allocLeftovers;
|
||||
int nextLeftover;
|
||||
/*@keep@*/
|
||||
const struct poptOption * options;
|
||||
int restLeftover;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char * appName;
|
||||
/*@only@*/ /*@null@*/
|
||||
poptItem aliases;
|
||||
int numAliases;
|
||||
int flags;
|
||||
/*@owned@*/ /*@null@*/
|
||||
unsigned int flags;
|
||||
poptItem execs;
|
||||
int numExecs;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char ** finalArgv;
|
||||
char * execFail;
|
||||
poptArgv finalArgv;
|
||||
int finalArgvCount;
|
||||
int finalArgvAlloced;
|
||||
/*@dependent@*/ /*@null@*/
|
||||
int (*maincall) (int argc, const char **argv);
|
||||
poptItem doExec;
|
||||
/*@only@*/
|
||||
const char * execPath;
|
||||
int execAbsolute;
|
||||
/*@only@*/ /*@relnull@*/
|
||||
const char * otherHelp;
|
||||
/*@null@*/
|
||||
pbm_set * arg_strip;
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBINTL_H
|
||||
#if defined(POPT_fprintf)
|
||||
#define POPT_dgettext dgettext
|
||||
#else
|
||||
#ifdef HAVE_ICONV
|
||||
#include <iconv.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DCGETTEXT)
|
||||
char *POPT_dgettext(const char * dom, const char * str);
|
||||
#endif
|
||||
|
||||
FORMAT(printf, 2, 3)
|
||||
int POPT_fprintf (FILE* stream, const char *format, ...);
|
||||
#endif /* !defined(POPT_fprintf) */
|
||||
|
||||
const char *POPT_prev_char (const char *str);
|
||||
const char *POPT_next_char (const char *str);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H)
|
||||
#include <libintl.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
|
||||
#define _(foo) gettext(foo)
|
||||
#else
|
||||
#define _(foo) foo
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
|
||||
#define D_(dom, str) dgettext(dom, str)
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
|
||||
#define D_(dom, str) POPT_dgettext(dom, str)
|
||||
#define POPT_(foo) D_("popt", foo)
|
||||
#else
|
||||
#define D_(dom, str) str
|
||||
@@ -119,4 +156,3 @@ struct poptContext_s {
|
||||
|
||||
#define N_(foo) foo
|
||||
|
||||
#endif
|
||||
+55
-45
@@ -1,5 +1,5 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/poptparse.c
|
||||
* @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -8,11 +8,8 @@
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include "poptint.h"
|
||||
|
||||
#define POPT_ARGV_ARRAY_GROW_DELTA 5
|
||||
|
||||
/*@-boundswrite@*/
|
||||
int poptDupArgv(int argc, const char **argv,
|
||||
int * argcPtr, const char *** argvPtr)
|
||||
{
|
||||
@@ -34,13 +31,13 @@ int poptDupArgv(int argc, const char **argv,
|
||||
return POPT_ERROR_MALLOC;
|
||||
argv2 = (void *) dst;
|
||||
dst += (argc + 1) * sizeof(*argv);
|
||||
*dst = '\0';
|
||||
|
||||
/*@-branchstate@*/
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv2[i] = dst;
|
||||
dst += strlcpy(dst, argv[i], nb) + 1;
|
||||
dst = stpcpy(dst, argv[i]);
|
||||
dst++; /* trailing NUL */
|
||||
}
|
||||
/*@=branchstate@*/
|
||||
argv2[argc] = NULL;
|
||||
|
||||
if (argvPtr) {
|
||||
@@ -53,21 +50,25 @@ int poptDupArgv(int argc, const char **argv,
|
||||
*argcPtr = argc;
|
||||
return 0;
|
||||
}
|
||||
/*@=boundswrite@*/
|
||||
|
||||
/*@-bounds@*/
|
||||
int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
{
|
||||
const char * src;
|
||||
char quote = '\0';
|
||||
int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
|
||||
const char ** argv = malloc(sizeof(*argv) * argvAlloced);
|
||||
const char ** argv_tmp;
|
||||
int argc = 0;
|
||||
int buflen = strlen(s) + 1;
|
||||
char * buf = memset(alloca(buflen), 0, buflen);
|
||||
size_t buflen = strlen(s) + 1;
|
||||
char * buf, * bufOrig = NULL;
|
||||
int rc = POPT_ERROR_MALLOC;
|
||||
|
||||
if (argv == NULL) return rc;
|
||||
buf = bufOrig = calloc((size_t)1, buflen);
|
||||
if (buf == NULL) {
|
||||
free(argv);
|
||||
return rc;
|
||||
}
|
||||
argv[argc] = buf;
|
||||
|
||||
for (src = s; *src != '\0'; src++) {
|
||||
@@ -83,13 +84,14 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
if (*src != quote) *buf++ = '\\';
|
||||
}
|
||||
*buf++ = *src;
|
||||
} else if (isSpace(src)) {
|
||||
} else if (_isspaceptr(src)) {
|
||||
if (*argv[argc] != '\0') {
|
||||
buf++, argc++;
|
||||
if (argc == argvAlloced) {
|
||||
argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
|
||||
argv = realloc(argv, sizeof(*argv) * argvAlloced);
|
||||
if (argv == NULL) goto exit;
|
||||
argv_tmp = realloc(argv, sizeof(*argv) * argvAlloced);
|
||||
if (argv_tmp == NULL) goto exit;
|
||||
argv = argv_tmp;
|
||||
}
|
||||
argv[argc] = buf;
|
||||
}
|
||||
@@ -97,17 +99,17 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
case '"':
|
||||
case '\'':
|
||||
quote = *src;
|
||||
/*@switchbreak@*/ break;
|
||||
break;
|
||||
case '\\':
|
||||
src++;
|
||||
if (!*src) {
|
||||
rc = POPT_ERROR_BADQUOTE;
|
||||
goto exit;
|
||||
}
|
||||
/*@fallthrough@*/
|
||||
/* fallthrough */
|
||||
default:
|
||||
*buf++ = *src;
|
||||
/*@switchbreak@*/ break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,29 +120,30 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
|
||||
|
||||
exit:
|
||||
if (bufOrig) free(bufOrig);
|
||||
if (argv) free(argv);
|
||||
return rc;
|
||||
}
|
||||
/*@=bounds@*/
|
||||
|
||||
/* still in the dev stage.
|
||||
* return values, perhaps 1== file erro
|
||||
* return values, perhaps 1== file error
|
||||
* 2== line to long
|
||||
* 3== umm.... more?
|
||||
*/
|
||||
int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int flags))
|
||||
int poptConfigFileToString(FILE *fp, char ** argstrp,
|
||||
UNUSED(int flags))
|
||||
{
|
||||
char line[999];
|
||||
char * argstr;
|
||||
char * argstr_tmp;
|
||||
char * p;
|
||||
char * q;
|
||||
char * x;
|
||||
int t;
|
||||
int argvlen = 0;
|
||||
size_t t;
|
||||
size_t argvlen = 0;
|
||||
size_t maxlinelen = sizeof(line);
|
||||
size_t linelen;
|
||||
int maxargvlen = 480;
|
||||
int linenum = 0;
|
||||
size_t maxargvlen = (size_t)480;
|
||||
|
||||
*argstrp = NULL;
|
||||
|
||||
@@ -155,11 +158,10 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
|
||||
if (argstr == NULL) return POPT_ERROR_MALLOC;
|
||||
|
||||
while (fgets(line, (int)maxlinelen, fp) != NULL) {
|
||||
linenum++;
|
||||
p = line;
|
||||
|
||||
/* loop until first non-space char or EOL */
|
||||
while( *p != '\0' && isSpace(p) )
|
||||
while( *p != '\0' && _isspaceptr(p) )
|
||||
p++;
|
||||
|
||||
linelen = strlen(p);
|
||||
@@ -173,25 +175,29 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
|
||||
|
||||
q = p;
|
||||
|
||||
while (*q != '\0' && (!isSpace(q)) && *q != '=')
|
||||
while (*q != '\0' && (!_isspaceptr(q)) && *q != '=')
|
||||
q++;
|
||||
|
||||
if (isSpace(q)) {
|
||||
if (_isspaceptr(q)) {
|
||||
/* a space after the name, find next non space */
|
||||
*q++='\0';
|
||||
while( *q != '\0' && isSpace(q) ) q++;
|
||||
while( *q != '\0' && _isspaceptr(q) ) q++;
|
||||
}
|
||||
if (*q == '\0') {
|
||||
/* single command line option (ie, no name=val, just name) */
|
||||
q[-1] = '\0'; /* kill off newline from fgets() call */
|
||||
argvlen += (t = q - p) + (sizeof(" --")-1);
|
||||
argvlen += (t = (size_t)(q - p)) + (sizeof(" --")-1);
|
||||
if (argvlen >= maxargvlen) {
|
||||
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
|
||||
argstr = realloc(argstr, maxargvlen);
|
||||
if (argstr == NULL) return POPT_ERROR_MALLOC;
|
||||
argstr_tmp = realloc(argstr, maxargvlen);
|
||||
if (argstr_tmp == NULL) {
|
||||
free(argstr);
|
||||
return POPT_ERROR_MALLOC;
|
||||
}
|
||||
argstr = argstr_tmp;
|
||||
}
|
||||
strlcat(argstr, " --", maxargvlen);
|
||||
strlcat(argstr, p, maxargvlen);
|
||||
strcat(argstr, " --");
|
||||
strcat(argstr, p);
|
||||
continue;
|
||||
}
|
||||
if (*q != '=')
|
||||
@@ -201,29 +207,33 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
|
||||
*q++ = '\0';
|
||||
|
||||
/* find next non-space letter of value */
|
||||
while (*q != '\0' && isSpace(q))
|
||||
while (*q != '\0' && _isspaceptr(q))
|
||||
q++;
|
||||
if (*q == '\0')
|
||||
continue; /* XXX silently ignore missing value */
|
||||
|
||||
/* now, loop and strip all ending whitespace */
|
||||
x = p + linelen;
|
||||
while (isSpace(--x))
|
||||
*x = 0; /* null out last char if space (including fgets() NL) */
|
||||
while (_isspaceptr(--x))
|
||||
*x = '\0'; /* null out last char if space (including fgets() NL) */
|
||||
|
||||
/* rest of line accept */
|
||||
t = x - p;
|
||||
t = (size_t)(x - p);
|
||||
argvlen += t + (sizeof("' --='")-1);
|
||||
if (argvlen >= maxargvlen) {
|
||||
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
|
||||
argstr = realloc(argstr, maxargvlen);
|
||||
if (argstr == NULL) return POPT_ERROR_MALLOC;
|
||||
argstr_tmp = realloc(argstr, maxargvlen);
|
||||
if (argstr_tmp == NULL) {
|
||||
free(argstr);
|
||||
return POPT_ERROR_MALLOC;
|
||||
}
|
||||
argstr = argstr_tmp;
|
||||
}
|
||||
strlcat(argstr, " --", maxargvlen);
|
||||
strlcat(argstr, p, maxargvlen);
|
||||
strlcat(argstr, "=\"", maxargvlen);
|
||||
strlcat(argstr, q, maxargvlen);
|
||||
strlcat(argstr, "\"", maxargvlen);
|
||||
strcat(argstr, " --");
|
||||
strcat(argstr, p);
|
||||
strcat(argstr, "=\"");
|
||||
strcat(argstr, q);
|
||||
strcat(argstr, "\"");
|
||||
}
|
||||
|
||||
*argstrp = argstr;
|
||||
|
||||
+45
-109
@@ -1,134 +1,70 @@
|
||||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if defined (__GLIBC__) && defined(__LCLINT__)
|
||||
/*@-declundef@*/
|
||||
/*@unchecked@*/
|
||||
extern __const __int32_t *__ctype_tolower;
|
||||
/*@unchecked@*/
|
||||
extern __const __int32_t *__ctype_toupper;
|
||||
/*@=declundef@*/
|
||||
#endif
|
||||
|
||||
#ifdef __TANDEM
|
||||
# include <floss.h(floss_execvp,floss_read)>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
/* XXX isspace(3) has i18n encoding signedness issues on Solaris. */
|
||||
#define _isspaceptr(_chp) isspace((int)(*(unsigned const char *)(_chp)))
|
||||
|
||||
#if HAVE_MCHECK_H
|
||||
#ifdef HAVE_MCHECK_H
|
||||
#include <mcheck.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
# endif
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
void * xmalloc (size_t size);
|
||||
|
||||
#ifdef __NeXT
|
||||
/* access macros are not declared in non posix mode in unistd.h -
|
||||
don't try to use posix on NeXTstep 3.3 ! */
|
||||
#include <libc.h>
|
||||
#endif
|
||||
void * xcalloc (size_t nmemb, size_t size);
|
||||
|
||||
#if defined(__LCLINT__)
|
||||
/*@-declundef -incondefs @*/ /* LCL: missing annotation */
|
||||
/*@only@*/ /*@out@*/
|
||||
void * alloca (size_t __size)
|
||||
/*@ensures MaxSet(result) == (__size - 1) @*/
|
||||
/*@*/;
|
||||
/*@=declundef =incondefs @*/
|
||||
#endif
|
||||
void * xrealloc (void * ptr, size_t size);
|
||||
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifndef __GNUC__
|
||||
# if HAVE_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# ifdef _AIX
|
||||
#pragma alloca
|
||||
# else
|
||||
# ifdef HAVE_ALLOCA
|
||||
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
||||
char *alloca(size_t size);
|
||||
# endif
|
||||
# else
|
||||
# ifdef alloca
|
||||
# undef alloca
|
||||
# endif
|
||||
# define alloca(sz) malloc(sz) /* Kludge this for now */
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#elif !defined(alloca)
|
||||
#define alloca __builtin_alloca
|
||||
#endif
|
||||
char * xstrdup (const char *str);
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char *d, const char *s, size_t bufsize);
|
||||
#endif
|
||||
#if !defined(HAVE_STPCPY)
|
||||
/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */
|
||||
static inline char * stpcpy (char *dest, const char * src) {
|
||||
register char *d = dest;
|
||||
register const char *s = src;
|
||||
|
||||
#ifndef HAVE_STRLCAT
|
||||
size_t strlcat(char *d, const char *s, size_t bufsize);
|
||||
#endif
|
||||
|
||||
#if HAVE_MCHECK_H && defined(__GNUC__)
|
||||
static inline char *
|
||||
xstrdup(const char *s)
|
||||
{
|
||||
size_t memsize = strlen(s) + 1;
|
||||
char *ptr = malloc(memsize);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "virtual memory exhausted.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strlcpy(ptr, s, memsize);
|
||||
return ptr;
|
||||
do
|
||||
*d++ = *s;
|
||||
while (*s++ != '\0');
|
||||
return d - 1;
|
||||
}
|
||||
#else
|
||||
#define xstrdup(_str) strdup(_str)
|
||||
#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
|
||||
#endif
|
||||
|
||||
#if HAVE___SECURE_GETENV && !defined(__LCLINT__)
|
||||
/* Memory allocation via macro defs to get meaningful locations from mtrace() */
|
||||
#if defined(HAVE_MCHECK_H) && defined(__GNUC__)
|
||||
#define vmefail() (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
|
||||
#define xmalloc(_size) (malloc(_size) ? : vmefail())
|
||||
#define xcalloc(_nmemb, _size) (calloc((_nmemb), (_size)) ? : vmefail())
|
||||
#define xrealloc(_ptr, _size) (realloc((_ptr), (_size)) ? : vmefail())
|
||||
#define xstrdup(_str) (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
|
||||
#else
|
||||
#define xmalloc(_size) malloc(_size)
|
||||
#define xcalloc(_nmemb, _size) calloc((_nmemb), (_size))
|
||||
#define xrealloc(_ptr, _size) realloc((_ptr), (_size))
|
||||
#define xstrdup(_str) strdup(_str)
|
||||
#endif /* defined(HAVE_MCHECK_H) && defined(__GNUC__) */
|
||||
|
||||
#if defined(HAVE_SECURE_GETENV)
|
||||
#define getenv(_s) secure_getenv(_s)
|
||||
#elif defined(HAVE___SECURE_GETENV)
|
||||
#define getenv(_s) __secure_getenv(_s)
|
||||
#endif
|
||||
|
||||
#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
|
||||
#define snprintf rsync_snprintf
|
||||
int snprintf(char *str,size_t count,const char *fmt,...);
|
||||
#if !defined(__GNUC__) && !defined(__attribute__)
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#define UNUSED(x) x __attribute__((__unused__))
|
||||
|
||||
#define PACKAGE "rsync"
|
||||
#define FORMAT(a, b, c) __attribute__((__format__ (a, b, c)))
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
|
||||
#include "popt.h"
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
#
|
||||
# build build the config files [the default w/no arg]
|
||||
# fetch fetch the latest dev autoconfig files
|
||||
# fetchgen fetch all the latest dev generated files (including man pages)
|
||||
# fetchgen fetch all the latest dev generated files (including manpages)
|
||||
# fetchSRC fetch the latest dev source files [NON-GENERATED FILES]
|
||||
#
|
||||
# The script stops after the first successful action.
|
||||
|
||||
+6
-6
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2000 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
|
||||
@@ -115,13 +115,13 @@ static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, int is_l
|
||||
units = "kB/s";
|
||||
}
|
||||
|
||||
if (remain < 0)
|
||||
if (remain < 0 || remain > 9999.0 * 3600.0)
|
||||
strlcpy(rembuf, " ??:??:??", sizeof rembuf);
|
||||
else {
|
||||
snprintf(rembuf, sizeof rembuf, "%4d:%02d:%02d",
|
||||
(int) (remain / 3600.0),
|
||||
(int) (remain / 60.0) % 60,
|
||||
(int) remain % 60);
|
||||
snprintf(rembuf, sizeof rembuf, "%4u:%02u:%02u",
|
||||
(unsigned int) (remain / 3600.0),
|
||||
(unsigned int) (remain / 60.0) % 60,
|
||||
(unsigned int) remain % 60);
|
||||
}
|
||||
|
||||
output_needs_newline = 0;
|
||||
|
||||
+192
-50
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996-2000 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2003-2021 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
|
||||
@@ -56,7 +56,6 @@ extern int inplace;
|
||||
extern int inplace_partial;
|
||||
extern int allowed_lull;
|
||||
extern int delay_updates;
|
||||
extern int xfersum_type;
|
||||
extern BOOL want_progress_now;
|
||||
extern mode_t orig_umask;
|
||||
extern struct stats stats;
|
||||
@@ -67,6 +66,11 @@ extern char sender_file_sum[MAX_DIGEST_LEN];
|
||||
extern struct file_list *cur_flist, *first_flist, *dir_flist;
|
||||
extern filter_rule_list daemon_filter_list;
|
||||
extern OFF_T preallocated_len;
|
||||
extern int fuzzy_basis;
|
||||
|
||||
extern struct name_num_item *xfer_sum_nni;
|
||||
extern int xfer_sum_len;
|
||||
extern int use_secure_symlinks;
|
||||
|
||||
static struct bitbag *delayed_bits = NULL;
|
||||
static int phase = 0, redoing = 0;
|
||||
@@ -79,6 +83,65 @@ static int updating_basis_or_equiv;
|
||||
#define MAX_UNIQUE_NUMBER 999999
|
||||
#define MAX_UNIQUE_LOOP 100
|
||||
|
||||
/* Open a basis/output path that may legitimately be an operator-trusted
|
||||
* ABSOLUTE path -- e.g. an absolute --partial-dir ("a directory reserved for
|
||||
* partial-dir work") or --backup-dir. secure_relative_open() deliberately
|
||||
* rejects an absolute relpath, so feeding it the whole absolute partialptr
|
||||
* (with a NULL basedir) returns EINVAL: the basis fd is then -1, no basis is
|
||||
* mapped, and receive_data() omits every matched block from the whole-file
|
||||
* verification checksum -> a spurious "failed verification" that strands the
|
||||
* (correct) data in the partial-dir forever.
|
||||
*
|
||||
* The operator's directory is trusted; only the leaf basename is peer-supplied.
|
||||
* So when basedir is NULL and relpath is absolute, split it into its directory
|
||||
* (trusted) and leaf and confine just the leaf -- exactly how secure_relative_
|
||||
* open already trusts an absolute basedir while O_NOFOLLOW-confining the leaf.
|
||||
* Anything else is a straight pass-through that preserves the strict contract. */
|
||||
static int secure_basis_open(const char *basedir, const char *relpath, int flags, mode_t mode)
|
||||
{
|
||||
extern int am_daemon, am_chrooted;
|
||||
|
||||
/* The confined resolver is only needed for the sanitizing daemon
|
||||
* (am_daemon && !am_chrooted, i.e. use_secure_symlinks). Local /
|
||||
* remote-shell mode has no module boundary, and "use chroot = yes" makes
|
||||
* the kernel root the boundary, so there an alt-dest basis like
|
||||
* --link-dest=../01 must resolve against the cwd as a bare open did before
|
||||
* the hardening (confining it would reject the legitimate sibling "..",
|
||||
* #915). */
|
||||
if (!am_daemon || am_chrooted) {
|
||||
if (basedir) {
|
||||
char fullpath[MAXPATHLEN];
|
||||
if (pathjoin(fullpath, sizeof fullpath, basedir, relpath) >= sizeof fullpath) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
return do_open(fullpath, flags, mode);
|
||||
}
|
||||
return do_open(relpath, flags, mode);
|
||||
}
|
||||
|
||||
if (!basedir && relpath && *relpath == '/') {
|
||||
const char *slash = strrchr(relpath, '/');
|
||||
const char *leaf = slash + 1;
|
||||
char dirbuf[MAXPATHLEN];
|
||||
const char *dir;
|
||||
if (slash == relpath)
|
||||
dir = "/";
|
||||
else {
|
||||
size_t dlen = slash - relpath;
|
||||
if (dlen >= sizeof dirbuf) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
memcpy(dirbuf, relpath, dlen);
|
||||
dirbuf[dlen] = '\0';
|
||||
dir = dirbuf;
|
||||
}
|
||||
return secure_relative_open(dir, leaf, flags, mode);
|
||||
}
|
||||
return secure_relative_open(basedir, relpath, flags, mode);
|
||||
}
|
||||
|
||||
/* get_tmpname() - create a tmp filename for a given filename
|
||||
*
|
||||
* If a tmpdir is defined, use that as the directory to put it in. Otherwise,
|
||||
@@ -211,7 +274,12 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
|
||||
* access to ensure that there is no race condition. They will be
|
||||
* correctly updated after the right owner and group info is set.
|
||||
* (Thanks to snabb@epipe.fi for pointing this out.) */
|
||||
fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
|
||||
/* When use_secure_symlinks is on (non-chroot daemon with munge_symlinks),
|
||||
* use secure_mkstemp to prevent symlink race attacks on parent directories. */
|
||||
if (use_secure_symlinks)
|
||||
fd = secure_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
|
||||
else
|
||||
fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
|
||||
|
||||
#if 0
|
||||
/* In most cases parent directories will already exist because their
|
||||
@@ -240,7 +308,6 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
static char file_sum1[MAX_DIGEST_LEN];
|
||||
struct map_struct *mapbuf;
|
||||
struct sum_struct sum;
|
||||
int sum_len;
|
||||
int32 len;
|
||||
OFF_T total_size = F_LENGTH(file);
|
||||
OFF_T offset = 0;
|
||||
@@ -280,7 +347,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
} else
|
||||
mapbuf = NULL;
|
||||
|
||||
sum_init(xfersum_type, checksum_seed);
|
||||
sum_init(xfer_sum_nni, checksum_seed);
|
||||
|
||||
if (append_mode > 0) {
|
||||
OFF_T j;
|
||||
@@ -310,7 +377,12 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
}
|
||||
}
|
||||
|
||||
while ((i = recv_token(f_in, &data)) != 0) {
|
||||
while (1) {
|
||||
data = NULL;
|
||||
i = recv_token(f_in, &data);
|
||||
if (i == 0)
|
||||
break;
|
||||
|
||||
if (INFO_GTE(PROGRESS, 1))
|
||||
show_progress(offset, total_size);
|
||||
|
||||
@@ -318,6 +390,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
|
||||
|
||||
if (i > 0) {
|
||||
if (!data) {
|
||||
rprintf(FERROR, "Invalid literal token with no data [%s]\n", who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (DEBUG_GTE(DELTASUM, 3)) {
|
||||
rprintf(FINFO,"data recv %d at %s\n",
|
||||
i, big_num(offset));
|
||||
@@ -335,6 +411,11 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
}
|
||||
|
||||
i = -(i+1);
|
||||
if (i < 0 || i >= sum.count) {
|
||||
rprintf(FERROR, "Invalid block index %d (count=%ld) [%s]\n",
|
||||
i, (long)sum.count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
offset2 = i * (OFF_T)sum.blength;
|
||||
len = sum.blength;
|
||||
if (i == (int)sum.count-1 && sum.remainder != 0)
|
||||
@@ -342,6 +423,34 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
|
||||
stats.matched_data += len;
|
||||
|
||||
/* A block match with no mapped basis is a protocol inconsistency
|
||||
* ONLY when we are actually producing output (fd != -1): the
|
||||
* generator told the sender a basis existed but the receiver could
|
||||
* not open it, so honoring the match would silently omit these
|
||||
* bytes from the verification checksum (a spurious failure) or
|
||||
* leave a hole in the output. Fail cleanly in that case.
|
||||
*
|
||||
* On the DISCARD path (fd == -1, fname == NULL) there is no output
|
||||
* and no verification: discard_receive_data() deliberately drains a
|
||||
* delta the receiver never intends to write (basis fstat failed,
|
||||
* basis is a directory, output open failed, batch skip, ...). The
|
||||
* sender does not know the data is being discarded and streams an
|
||||
* ordinary delta, so a match token here is NORMAL protocol, not
|
||||
* malformed. Absorb it benignly (advance the offset and continue),
|
||||
* as the pre-existing "if (mapbuf)" guards did before this check was
|
||||
* added in 31fbb17d -- erroring would wrongly break legitimate
|
||||
* transfers, and full_fname(fname) with fname==NULL would
|
||||
* dereference NULL (a receiver crash on a normal transfer). */
|
||||
if (!mapbuf) {
|
||||
if (fd != -1) {
|
||||
rprintf(FERROR, "got a block match with no basis file for %s [%s]\n",
|
||||
full_fname(fname), who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
offset += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DEBUG_GTE(DELTASUM, 3)) {
|
||||
rprintf(FINFO,
|
||||
"chunk[%d] of size %ld at %s offset=%s%s\n",
|
||||
@@ -371,7 +480,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
|
||||
if (fd != -1 && offset > 0) {
|
||||
if (sparse_files > 0) {
|
||||
if (sparse_end(fd, offset) != 0)
|
||||
if (sparse_end(fd, offset, updating_basis_or_equiv) != 0)
|
||||
goto report_write_error;
|
||||
} else if (flush_write_file(fd) < 0) {
|
||||
report_write_error:
|
||||
@@ -393,7 +502,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
if (INFO_GTE(PROGRESS, 1))
|
||||
end_progress(total_size);
|
||||
|
||||
sum_len = sum_end(file_sum1);
|
||||
sum_end(file_sum1);
|
||||
|
||||
if (do_fsync && fd != -1 && fsync(fd) != 0) {
|
||||
rsyserr(FERROR, errno, "fsync failed on %s", full_fname(fname));
|
||||
@@ -403,10 +512,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
if (mapbuf)
|
||||
unmap_file(mapbuf);
|
||||
|
||||
read_buf(f_in, sender_file_sum, sum_len);
|
||||
read_buf(f_in, sender_file_sum, xfer_sum_len);
|
||||
if (DEBUG_GTE(DELTASUM, 2))
|
||||
rprintf(FINFO,"got file_sum\n");
|
||||
if (fd != -1 && memcmp(file_sum1, sender_file_sum, sum_len) != 0)
|
||||
if (fd != -1 && memcmp(file_sum1, sender_file_sum, xfer_sum_len) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -434,14 +543,13 @@ static void handle_delayed_updates(char *local_name)
|
||||
}
|
||||
/* We don't use robust_rename() here because the
|
||||
* partial-dir must be on the same drive. */
|
||||
if (do_rename(partialptr, fname) < 0) {
|
||||
if (do_rename_at(partialptr, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"rename failed for %s (from %s)",
|
||||
full_fname(fname), partialptr);
|
||||
} else {
|
||||
if (remove_source_files
|
||||
|| (preserve_hard_links && F_IS_HLINKED(file)))
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
if (remove_source_files || (preserve_hard_links && F_IS_HLINKED(file)))
|
||||
send_msg_success(fname, ndx);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
}
|
||||
@@ -451,7 +559,10 @@ static void handle_delayed_updates(char *local_name)
|
||||
static void no_batched_update(int ndx, BOOL is_redo)
|
||||
{
|
||||
struct file_list *flist = flist_for_ndx(ndx, "no_batched_update");
|
||||
struct file_struct *file = flist->files[ndx - flist->ndx_start];
|
||||
struct file_struct *file;
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
file = flist->files[ndx - flist->ndx_start];
|
||||
|
||||
rprintf(FERROR_XFER, "(No batched update for%s \"%s\")\n",
|
||||
is_redo ? " resend of" : "", f_name(file, NULL));
|
||||
@@ -551,6 +662,8 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
progress_init();
|
||||
|
||||
while (1) {
|
||||
const char *basedir = NULL;
|
||||
|
||||
cleanup_disable();
|
||||
|
||||
/* This call also sets cur_flist. */
|
||||
@@ -586,6 +699,8 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
|
||||
if (ndx - cur_flist->ndx_start >= 0)
|
||||
file = cur_flist->files[ndx - cur_flist->ndx_start];
|
||||
else if (cur_flist->parent_ndx < 0)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
else
|
||||
file = dir_flist->files[cur_flist->parent_ndx];
|
||||
fname = local_name ? local_name : f_name(file, fbuf);
|
||||
@@ -593,10 +708,13 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
if (DEBUG_GTE(RECV, 1))
|
||||
rprintf(FINFO, "recv_files(%s)\n", fname);
|
||||
|
||||
if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')
|
||||
&& check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) {
|
||||
rprintf(FERROR, "attempt to hack rsync failed.\n");
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')) {
|
||||
int filt_flags = S_ISDIR(file->mode) ? NAME_IS_DIR : NAME_IS_FILE;
|
||||
if (check_filter(&daemon_filter_list, FLOG, fname, filt_flags) < 0) {
|
||||
rprintf(FERROR, "ERROR: rejecting file transfer request for daemon excluded file: %s\n",
|
||||
fname);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_XATTRS
|
||||
@@ -695,7 +813,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
if (!am_server)
|
||||
discard_receive_data(f_in, file);
|
||||
if (inc_recurse)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
send_msg_success(fname, ndx);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -713,28 +831,34 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
fnamecmp = get_backup_name(fname);
|
||||
break;
|
||||
case FNAMECMP_FUZZY:
|
||||
if (fuzzy_basis == 0) {
|
||||
rprintf(FERROR_XFER, "rsync: refusing malicious fuzzy operation for %s\n", xname);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (file->dirname) {
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
} else
|
||||
fnamecmp = xname;
|
||||
basedir = file->dirname;
|
||||
}
|
||||
fnamecmp = xname;
|
||||
break;
|
||||
default:
|
||||
if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
|
||||
fnamecmp_type -= FNAMECMP_FUZZY + 1;
|
||||
if (file->dirname) {
|
||||
stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
|
||||
basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
|
||||
} else
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], file->dirname);
|
||||
basedir = fnamecmpbuf;
|
||||
} else {
|
||||
basedir = basis_dir[fnamecmp_type];
|
||||
}
|
||||
fnamecmp = xname;
|
||||
} else if (fnamecmp_type >= basis_dir_cnt) {
|
||||
rprintf(FERROR,
|
||||
"invalid basis_dir index: %d.\n",
|
||||
fnamecmp_type);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
} else
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
} else {
|
||||
basedir = basis_dir[fnamecmp_type];
|
||||
fnamecmp = fname;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!fnamecmp || (daemon_filter_list.head
|
||||
@@ -756,26 +880,33 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
fnamecmp = fname;
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
/* open the file (secure_basis_open tolerates an operator-trusted
|
||||
* absolute fnamecmp, e.g. an absolute --partial-dir basis) */
|
||||
fd1 = secure_basis_open(basedir, fnamecmp, O_RDONLY, 0);
|
||||
|
||||
if (fd1 == -1 && protocol_version < 29) {
|
||||
if (fnamecmp != fname) {
|
||||
fnamecmp = fname;
|
||||
fnamecmp_type = FNAMECMP_FNAME;
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
fd1 = do_open_nofollow(fnamecmp, O_RDONLY);
|
||||
}
|
||||
|
||||
if (fd1 == -1 && basis_dir[0]) {
|
||||
/* pre-29 allowed only one alternate basis */
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
|
||||
basis_dir[0], fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
basedir = basis_dir[0];
|
||||
fnamecmp = fname;
|
||||
fnamecmp_type = FNAMECMP_BASIS_DIR_LOW;
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
fd1 = secure_basis_open(basedir, fnamecmp, O_RDONLY, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (basedir) {
|
||||
// for the following code we need the full
|
||||
// path name as a single string
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basedir, fnamecmp);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
}
|
||||
|
||||
one_inplace = inplace_partial && fnamecmp_type == FNAMECMP_PARTIAL_DIR;
|
||||
updating_basis_or_equiv = one_inplace
|
||||
|| (inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP));
|
||||
@@ -808,14 +939,16 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd1 != -1 && !(S_ISREG(st.st_mode) || (write_devices && IS_DEVICE(st.st_mode)))) {
|
||||
if (write_devices && IS_DEVICE(st.st_mode)) {
|
||||
if (fd1 != -1 && st.st_size == 0)
|
||||
st.st_size = get_device_size(fd1, fname);
|
||||
/* Mark the file entry as a device so that we don't try to truncate it later on. */
|
||||
file->mode = S_IFBLK | (file->mode & ACCESSPERMS);
|
||||
} else if (fd1 != -1 && !(S_ISREG(st.st_mode))) {
|
||||
close(fd1);
|
||||
fd1 = -1;
|
||||
}
|
||||
|
||||
if (fd1 != -1 && IS_DEVICE(st.st_mode) && st.st_size == 0)
|
||||
st.st_size = get_device_size(fd1, fname);
|
||||
|
||||
/* If we're not preserving permissions, change the file-list's
|
||||
* mode based on the local permissions and some heuristics. */
|
||||
if (!preserve_perms) {
|
||||
@@ -834,11 +967,21 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
/* We now check to see if we are writing the file "inplace" */
|
||||
if (inplace || one_inplace) {
|
||||
fnametmp = one_inplace ? partialptr : fname;
|
||||
fd2 = do_open(fnametmp, O_WRONLY|O_CREAT, 0600);
|
||||
/* When use_secure_symlinks is on (non-chroot daemon),
|
||||
* use secure open to prevent symlink race attacks where an
|
||||
* attacker could switch a directory to a symlink between
|
||||
* path validation and file open. */
|
||||
if (use_secure_symlinks)
|
||||
fd2 = secure_basis_open(NULL, fnametmp, O_WRONLY|O_CREAT, 0600);
|
||||
else
|
||||
fd2 = do_open(fnametmp, O_WRONLY|O_CREAT, 0600);
|
||||
#ifdef linux
|
||||
if (fd2 == -1 && errno == EACCES) {
|
||||
/* Maybe the error was due to protected_regular setting? */
|
||||
fd2 = do_open(fname, O_WRONLY, 0600);
|
||||
if (use_secure_symlinks)
|
||||
fd2 = secure_relative_open(NULL, fname, O_WRONLY, 0600);
|
||||
else
|
||||
fd2 = do_open(fname, O_WRONLY, 0600);
|
||||
}
|
||||
#endif
|
||||
if (fd2 == -1) {
|
||||
@@ -890,7 +1033,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
recv_ok = -1;
|
||||
else if (fnamecmp == partialptr) {
|
||||
if (!one_inplace)
|
||||
do_unlink(partialptr);
|
||||
do_unlink_at(partialptr);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
} else if (keep_partial && partialptr && (!one_inplace || delay_updates)) {
|
||||
@@ -899,7 +1042,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
"Unable to create partial-dir for %s -- discarding %s.\n",
|
||||
local_name ? local_name : f_name(file, NULL),
|
||||
recv_ok ? "completed file" : "partial file");
|
||||
do_unlink(fnametmp);
|
||||
do_unlink_at(fnametmp);
|
||||
recv_ok = -1;
|
||||
} else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
|
||||
file, recv_ok, !partial_dir))
|
||||
@@ -910,7 +1053,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
} else
|
||||
partialptr = NULL;
|
||||
} else if (!one_inplace)
|
||||
do_unlink(fnametmp);
|
||||
do_unlink_at(fnametmp);
|
||||
|
||||
cleanup_disable();
|
||||
|
||||
@@ -921,9 +1064,8 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
case 2:
|
||||
break;
|
||||
case 1:
|
||||
if (remove_source_files || inc_recurse
|
||||
|| (preserve_hard_links && F_IS_HLINKED(file)))
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file)))
|
||||
send_msg_success(fname, ndx);
|
||||
break;
|
||||
case 0: {
|
||||
enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;
|
||||
|
||||
+68
-39
@@ -1,14 +1,17 @@
|
||||
# NAME
|
||||
## NAME
|
||||
|
||||
rsync-ssl - a helper script for connecting to an ssl rsync daemon
|
||||
|
||||
# SYNOPSIS
|
||||
## SYNOPSIS
|
||||
|
||||
```
|
||||
rsync-ssl [--type=SSL_TYPE] RSYNC_ARGS
|
||||
```
|
||||
|
||||
# DESCRIPTION
|
||||
The online version of this manpage (that includes cross-linking of topics)
|
||||
is available at <https://download.samba.org/pub/rsync/rsync-ssl.1>.
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
The rsync-ssl script helps you to run an rsync copy to/from an rsync daemon
|
||||
that requires ssl connections.
|
||||
@@ -20,7 +23,7 @@ environment. You can specify an overriding port via `--port` or by including
|
||||
it in the normal spot in the URL format, though both of those require your
|
||||
rsync version to be at least 3.2.0.
|
||||
|
||||
# OPTIONS
|
||||
## OPTIONS
|
||||
|
||||
If the **first** arg is a `--type=SSL_TYPE` option, the script will only use
|
||||
that particular program to open an ssl connection instead of trying to find an
|
||||
@@ -32,35 +35,56 @@ required for this particular option.
|
||||
All the other options are passed through to the rsync command, so consult the
|
||||
**rsync**(1) manpage for more information on how it works.
|
||||
|
||||
# ENVIRONMENT VARIABLES
|
||||
## ENVIRONMENT VARIABLES
|
||||
|
||||
The ssl helper scripts are affected by the following environment variables:
|
||||
|
||||
0. `RSYNC_SSL_TYPE` Specifies the program type that should be used to open the
|
||||
ssl connection. It must be one of `openssl` or `stunnel`. The
|
||||
`--type=SSL_TYPE` option overrides this, when specified.
|
||||
0. `RSYNC_SSL_PORT` If specified, the value is the port number that is used as
|
||||
the default when the user does not specify a port in their rsync command.
|
||||
When not specified, the default port number is 874. (Note that older rsync
|
||||
versions (prior to 3.2.0) did not communicate an overriding port number
|
||||
value to the helper script.)
|
||||
0. `RSYNC_SSL_CERT` If specified, the value is a filename that contains a
|
||||
certificate to use for the connection.
|
||||
0. `RSYNC_SSL_KEY` If specified, the value is a filename that contains a
|
||||
key for the provided certificate to use for the connection.
|
||||
0. `RSYNC_SSL_CA_CERT` If specified, the value is a filename that contains a
|
||||
certificate authority certificate that is used to validate the connection.
|
||||
0. `RSYNC_SSL_OPENSSL` Specifies the openssl executable to run when the
|
||||
connection type is set to openssl. If unspecified, the $PATH is searched
|
||||
for "openssl".
|
||||
0. `RSYNC_SSL_GNUTLS` Specifies the gnutls-cli executable to run when the
|
||||
connection type is set to gnutls. If unspecified, the $PATH is searched
|
||||
for "gnutls-cli".
|
||||
0. `RSYNC_SSL_STUNNEL` Specifies the stunnel executable to run when the
|
||||
connection type is set to stunnel. If unspecified, the $PATH is searched
|
||||
first for "stunnel4" and then for "stunnel".
|
||||
0. `RSYNC_SSL_TYPE`
|
||||
|
||||
# EXAMPLES
|
||||
Specifies the program type that should be used to open the ssl connection.
|
||||
It must be one of `openssl` or `stunnel`. The `--type=SSL_TYPE` option
|
||||
overrides this, when specified.
|
||||
|
||||
0. `RSYNC_SSL_PORT`
|
||||
|
||||
If specified, the value is the port number that is used as the default when
|
||||
the user does not specify a port in their rsync command. When not
|
||||
specified, the default port number is 874. (Note that older rsync versions
|
||||
(prior to 3.2.0) did not communicate an overriding port number value to the
|
||||
helper script.)
|
||||
|
||||
0. `RSYNC_SSL_CERT`
|
||||
|
||||
If specified, the value is a filename that contains a certificate to use
|
||||
for the connection.
|
||||
|
||||
0. `RSYNC_SSL_KEY`
|
||||
|
||||
If specified, the value is a filename that contains a key for the provided
|
||||
certificate to use for the connection.
|
||||
|
||||
0. `RSYNC_SSL_CA_CERT`
|
||||
|
||||
If specified, the value is a filename that contains a certificate authority
|
||||
certificate that is used to validate the connection.
|
||||
|
||||
0. `RSYNC_SSL_OPENSSL`
|
||||
|
||||
Specifies the openssl executable to run when the connection type is set to
|
||||
openssl. If unspecified, the $PATH is searched for "openssl".
|
||||
|
||||
0. `RSYNC_SSL_GNUTLS`
|
||||
|
||||
Specifies the gnutls-cli executable to run when the connection type is set
|
||||
to gnutls. If unspecified, the $PATH is searched for "gnutls-cli".
|
||||
|
||||
0. `RSYNC_SSL_STUNNEL`
|
||||
|
||||
Specifies the stunnel executable to run when the connection type is set to
|
||||
stunnel. If unspecified, the $PATH is searched first for "stunnel4" and
|
||||
then for "stunnel".
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
> rsync-ssl -aiv example.com::mod/ dest
|
||||
|
||||
@@ -70,11 +94,16 @@ The ssl helper scripts are affected by the following environment variables:
|
||||
|
||||
> rsync-ssl -aiv rsync://example.com:9874/mod/ dest
|
||||
|
||||
# SEE ALSO
|
||||
## THE SERVER SIDE
|
||||
|
||||
**rsync**(1), **rsyncd.conf**(5)
|
||||
For help setting up an SSL/TLS supporting rsync, see the [instructions in
|
||||
rsyncd.conf](rsyncd.conf.5#SSL_TLS_Daemon_Setup).
|
||||
|
||||
# CAVEATS
|
||||
## SEE ALSO
|
||||
|
||||
[**rsync**(1)](rsync.1), [**rsyncd.conf**(5)](rsyncd.conf.5)
|
||||
|
||||
## CAVEATS
|
||||
|
||||
Note that using an stunnel connection requires at least version 4 of stunnel,
|
||||
which should be the case on modern systems. Also, it does not verify a
|
||||
@@ -87,23 +116,23 @@ release the gnutls-cli command was dropping output, making it unusable. If
|
||||
that bug has been fixed in your version, feel free to put gnutls into an
|
||||
exported RSYNC_SSL_TYPE environment variable to make its use the default.
|
||||
|
||||
# BUGS
|
||||
## BUGS
|
||||
|
||||
Please report bugs! See the web site at <https://rsync.samba.org/>.
|
||||
|
||||
# VERSION
|
||||
## VERSION
|
||||
|
||||
This man page is current for version @VERSION@ of rsync.
|
||||
This manpage is current for version @VERSION@ of rsync.
|
||||
|
||||
# CREDITS
|
||||
## CREDITS
|
||||
|
||||
rsync is distributed under the GNU General Public License. See the file
|
||||
COPYING for details.
|
||||
Rsync is distributed under the GNU General Public License. See the file
|
||||
[COPYING](COPYING) for details.
|
||||
|
||||
A web site is available at <https://rsync.samba.org/>. The site includes an
|
||||
FAQ-O-Matic which may cover questions unanswered by this manual page.
|
||||
|
||||
# AUTHOR
|
||||
## AUTHOR
|
||||
|
||||
This manpage was written by Wayne Davison.
|
||||
|
||||
|
||||
+1749
-1132
File diff suppressed because it is too large.
Load diff
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2003-2021 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
|
||||
@@ -437,7 +437,10 @@ int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr, cha
|
||||
*/
|
||||
void free_sums(struct sum_struct *s)
|
||||
{
|
||||
if (s->sums) free(s->sums);
|
||||
if (s->sums) {
|
||||
free(s->sums);
|
||||
free(s->sum2_array);
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
@@ -544,7 +547,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
|
||||
if (am_root >= 0) {
|
||||
uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
|
||||
gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
|
||||
if (do_lchown(fname, uid, gid) != 0) {
|
||||
if (do_lchown_at(fname, uid, gid) != 0) {
|
||||
/* We shouldn't have attempted to change uid
|
||||
* or gid unless have the privilege. */
|
||||
rsyserr(FERROR_XFER, errno, "%s %s failed",
|
||||
@@ -642,7 +645,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
|
||||
#ifdef SUPPORT_ACLS
|
||||
/* It's OK to call set_acl() now, even for a dir, as the generator
|
||||
* will enable owner-writability using chmod, if necessary.
|
||||
*
|
||||
*
|
||||
* If set_acl() changes permission bits in the process of setting
|
||||
* an access ACL, it changes sxp->st.st_mode so we know whether we
|
||||
* need to chmod(). */
|
||||
@@ -654,7 +657,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
|
||||
|
||||
#ifdef HAVE_CHMOD
|
||||
if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
|
||||
int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
|
||||
int ret = am_root < 0 ? 0 : do_chmod_at(fname, new_mode);
|
||||
if (ret < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to set permissions on %s",
|
||||
@@ -755,7 +758,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
|
||||
full_fname(fnametmp), fname);
|
||||
if (!partialptr || (ret == -2 && temp_copy_name)
|
||||
|| robust_rename(fnametmp, partialptr, NULL, file->mode) < 0)
|
||||
do_unlink(fnametmp);
|
||||
do_unlink_at(fnametmp);
|
||||
return 0;
|
||||
}
|
||||
if (ret == 0) {
|
||||
@@ -771,7 +774,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
|
||||
ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME);
|
||||
|
||||
if (temp_copy_name) {
|
||||
if (do_rename(fnametmp, fname) < 0) {
|
||||
if (do_rename_at(fnametmp, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\"",
|
||||
full_fname(fnametmp), fname);
|
||||
return 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (C) 1996, 2000 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-2021 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
|
||||
@@ -18,11 +18,6 @@
|
||||
* with this program; if not, visit the http://fsf.org website.
|
||||
*/
|
||||
|
||||
/* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
|
||||
incompatible with older versions :-( */
|
||||
#define CHAR_OFFSET 0
|
||||
|
||||
#ifndef AVX2_ASM /* do not include the rest of file for assembly */
|
||||
#define False 0
|
||||
#define True 1
|
||||
#define Unset (-1) /* Our BOOL values are always an int. */
|
||||
@@ -43,6 +38,9 @@
|
||||
|
||||
#define BACKUP_SUFFIX "~"
|
||||
|
||||
/* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
|
||||
incompatible with older versions :-( */
|
||||
#define CHAR_OFFSET 0
|
||||
|
||||
/* These flags are only used during the flist transfer. */
|
||||
|
||||
@@ -94,6 +92,7 @@
|
||||
#define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */
|
||||
#define FLAG_TIME_FAILED (1<<11)/* generator */
|
||||
#define FLAG_MOD_NSEC (1<<12) /* sender/receiver/generator */
|
||||
#define FLAG_GOT_DIR_FLIST (1<<13)/* sender/receiver/generator - dir_flist only */
|
||||
|
||||
/* These flags are passed to functions but not stored. */
|
||||
|
||||
@@ -112,7 +111,7 @@
|
||||
|
||||
/* Update this if you make incompatible changes and ALSO update the
|
||||
* SUBPROTOCOL_VERSION if it is not a final (official) release. */
|
||||
#define PROTOCOL_VERSION 31
|
||||
#define PROTOCOL_VERSION 32
|
||||
|
||||
/* This is used when working on a new protocol version or for any unofficial
|
||||
* protocol tweaks. It should be a non-zero value for each pre-release repo
|
||||
@@ -164,6 +163,29 @@
|
||||
/* For compatibility with older rsyncs */
|
||||
#define OLD_MAX_BLOCK_SIZE ((int32)1 << 29)
|
||||
|
||||
/* Policy ceilings on attacker-controlled wire values. Picked well above any
|
||||
* legitimate filesystem / protocol traffic but well below sizes that could
|
||||
* cause integer overflow or DoS-grade allocations. See input_checking.txt.
|
||||
*
|
||||
* Note on MAX_WIRE_XATTR_DATALEN: xattr datum size is bounded only by the
|
||||
* wire-format maximum (signed int32 varint, ~2GB). macOS resource forks
|
||||
* are transferred as the com.apple.ResourceFork xattr and can legitimately
|
||||
* be many GB; --max-alloc (default 1GB, configurable) is the real
|
||||
* allocation cap. read_varint_size() still rejects negative values so a
|
||||
* hostile peer cannot wrap to ~SIZE_MAX. */
|
||||
#define MAX_WIRE_XATTR_COUNT 65536
|
||||
#define MAX_WIRE_XATTR_NAMELEN 4096
|
||||
#define MAX_WIRE_XATTR_DATALEN ((int32)0x7fffffff)
|
||||
#define MAX_WIRE_ACL_COUNT 65536
|
||||
#define MAX_WIRE_NSEC 999999999
|
||||
/* MAX_WIRE_DEL_STAT is the per-category cap for read_del_stats() in main.c,
|
||||
* which accumulates 5 wire-supplied counts into the int32 stats.deleted_files
|
||||
* accumulator. Capped at 2^28 so 5 * 2^28 = 1.34 GB stays under INT32_MAX
|
||||
* (2.15 GB) with margin -- a higher cap (e.g. 2^30) would let a hostile peer
|
||||
* supplying 3+ max-sized counts overflow the accumulator, which is signed-int
|
||||
* UB. 2^28 is still well above any plausible real transfer's deletion count. */
|
||||
#define MAX_WIRE_DEL_STAT ((int32)1 << 28)
|
||||
|
||||
#define ROUND_UP_1024(siz) ((siz) & (1024-1) ? ((siz) | (1024-1)) + 1 : (siz))
|
||||
|
||||
#define IOERR_GENERAL (1<<0) /* For backward compatibility, this must == 1 */
|
||||
@@ -340,6 +362,9 @@ enum delret {
|
||||
# endif
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_BSD_STRING_H
|
||||
# include <bsd/string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
@@ -365,16 +390,10 @@ enum delret {
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifdef TIME_WITH_SYS_TIME
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#else
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#endif
|
||||
#include <time.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
@@ -825,6 +844,7 @@ extern int uid_ndx;
|
||||
extern int gid_ndx;
|
||||
extern int acls_ndx;
|
||||
extern int xattrs_ndx;
|
||||
extern int file_sum_extra_cnt;
|
||||
|
||||
#ifdef USE_FLEXIBLE_ARRAY
|
||||
#define FILE_STRUCT_LEN (sizeof (struct file_struct))
|
||||
@@ -835,7 +855,7 @@ extern int xattrs_ndx;
|
||||
#define DEV_EXTRA_CNT 2
|
||||
#define DIRNODE_EXTRA_CNT 3
|
||||
#define EXTRA64_CNT ((sizeof (union file_extras64) + EXTRA_LEN - 1) / EXTRA_LEN)
|
||||
#define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
|
||||
#define SUM_EXTRA_CNT file_sum_extra_cnt
|
||||
|
||||
#define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx))
|
||||
#define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump))
|
||||
@@ -962,12 +982,12 @@ struct sum_buf {
|
||||
uint32 sum1; /**< simple checksum */
|
||||
int32 chain; /**< next hash-table collision */
|
||||
short flags; /**< flag bits */
|
||||
char sum2[SUM_LENGTH]; /**< checksum */
|
||||
};
|
||||
|
||||
struct sum_struct {
|
||||
OFF_T flength; /**< total file length */
|
||||
struct sum_buf *sums; /**< points to info for each chunk */
|
||||
char *sum2_array; /**< checksums of length xfer_sum_len */
|
||||
int32 count; /**< how many chunks */
|
||||
int32 blength; /**< block_length */
|
||||
int32 remainder; /**< flength % block_length */
|
||||
@@ -986,6 +1006,8 @@ struct map_struct {
|
||||
int status; /* first errno from read errors */
|
||||
};
|
||||
|
||||
#define sum2_at(s, i) ((s)->sum2_array + ((size_t)(i) * xfer_sum_len))
|
||||
|
||||
#define NAME_IS_FILE (0) /* filter name as a file */
|
||||
#define NAME_IS_DIR (1<<0) /* filter name as a dir */
|
||||
#define NAME_IS_XATTR (1<<2) /* filter name as an xattr */
|
||||
@@ -1022,6 +1044,7 @@ typedef struct filter_struct {
|
||||
int slash_cnt;
|
||||
struct filter_list_struct *mergelist;
|
||||
} u;
|
||||
uchar elide;
|
||||
} filter_rule;
|
||||
|
||||
typedef struct filter_list_struct {
|
||||
@@ -1161,19 +1184,23 @@ typedef struct {
|
||||
#define NSTR_COMPRESS 1
|
||||
|
||||
struct name_num_item {
|
||||
int num;
|
||||
const char *name, *main_name;
|
||||
int num, flags;
|
||||
const char *name;
|
||||
struct name_num_item *main_nni;
|
||||
};
|
||||
|
||||
struct name_num_obj {
|
||||
const char *type;
|
||||
const char *negotiated_name;
|
||||
struct name_num_item *negotiated_nni;
|
||||
uchar *saw;
|
||||
int saw_len;
|
||||
int negotiated_num;
|
||||
struct name_num_item list[10]; /* we'll get a compile error/warning if this is ever too small */
|
||||
struct name_num_item *list;
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_ZLIB
|
||||
#define read_buf read_buf_
|
||||
#endif
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include "proto.h"
|
||||
#endif
|
||||
@@ -1477,7 +1504,6 @@ const char *get_panic_action(void);
|
||||
fprintf(stderr, "%s in %s at line %d\n", msg, __FILE__, __LINE__); \
|
||||
exit_cleanup(RERR_UNSUPPORTED); \
|
||||
} while (0)
|
||||
#endif /* AVX2_ASM */
|
||||
|
||||
#ifdef HAVE_MALLINFO2
|
||||
#define MEM_ALLOC_INFO mallinfo2
|
||||
|
||||
+189
-147
@@ -1,12 +1,15 @@
|
||||
# NAME
|
||||
## NAME
|
||||
|
||||
rsyncd.conf - configuration file for rsync in daemon mode
|
||||
|
||||
# SYNOPSIS
|
||||
## SYNOPSIS
|
||||
|
||||
rsyncd.conf
|
||||
|
||||
# DESCRIPTION
|
||||
The online version of this manpage (that includes cross-linking of topics)
|
||||
is available at <https://download.samba.org/pub/rsync/rsyncd.conf.5>.
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
The rsyncd.conf file is the runtime configuration file for rsync when run as an
|
||||
rsync daemon.
|
||||
@@ -14,7 +17,7 @@ rsync daemon.
|
||||
The rsyncd.conf file controls authentication, access, logging and available
|
||||
modules.
|
||||
|
||||
# FILE FORMAT
|
||||
## FILE FORMAT
|
||||
|
||||
The file consists of modules and parameters. A module begins with the name of
|
||||
the module in square brackets and continues until the next module begins.
|
||||
@@ -40,10 +43,9 @@ The values following the equals sign in parameters are all either a string (no
|
||||
quotes needed) or a boolean, which may be given as yes/no, 0/1 or true/false.
|
||||
Case is not significant in boolean values, but is preserved in string values.
|
||||
|
||||
# LAUNCHING THE RSYNC DAEMON
|
||||
## LAUNCHING THE RSYNC DAEMON
|
||||
|
||||
The rsync daemon is launched by specifying the `--daemon` option to
|
||||
rsync.
|
||||
The rsync daemon is launched by specifying the `--daemon` option to rsync.
|
||||
|
||||
The daemon must run with root privileges if you wish to use chroot, to bind to
|
||||
a port numbered under 1024 (as is the default 873), or to set file ownership.
|
||||
@@ -69,36 +71,18 @@ reread its config file.
|
||||
Note that you should **not** send the rsync daemon a HUP signal to force it to
|
||||
reread the `rsyncd.conf` file. The file is re-read on each client connection.
|
||||
|
||||
# GLOBAL PARAMETERS
|
||||
## GLOBAL PARAMETERS
|
||||
|
||||
The first parameters in the file (before a [module] header) are the global
|
||||
parameters. Rsync also allows for the use of a "[global]" module name to
|
||||
indicate the start of one or more global-parameter sections (the name must be
|
||||
lower case).
|
||||
|
||||
You may also include any module parameters in the global part of the config
|
||||
file in which case the supplied value will override the default for that
|
||||
parameter.
|
||||
|
||||
You may use references to environment variables in the values of parameters.
|
||||
String parameters will have %VAR% references expanded as late as possible (when
|
||||
the string is first used in the program), allowing for the use of variables
|
||||
that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
|
||||
parameters (such as true/false settings) are expanded when read from the config
|
||||
file. If a variable does not exist in the environment, or if a sequence of
|
||||
characters is not a valid reference (such as an un-paired percent sign), the
|
||||
raw characters are passed through unchanged. This helps with backward
|
||||
compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
|
||||
string in a path could result in a very unsafe path). The safest way to insert
|
||||
a literal % into a value is to use %%.
|
||||
parameters:
|
||||
|
||||
[comment]: # (An OL starting at 0 is converted into a DL by the parser.)
|
||||
|
||||
0. `motd file`
|
||||
|
||||
This parameter allows you to specify a "message of the day" to display to
|
||||
clients on each connect. This usually contains site information and any
|
||||
legal notices. The default is no motd file. This can be overridden by the
|
||||
This parameter allows you to specify a "message of the day" (MOTD) to display
|
||||
to clients on each connect. This usually contains site information and any
|
||||
legal notices. The default is no MOTD file. This can be overridden by the
|
||||
`--dparam=motdfile=FILE` command-line option when starting the daemon.
|
||||
|
||||
0. `pid file`
|
||||
@@ -126,7 +110,7 @@ a literal % into a value is to use %%.
|
||||
|
||||
This parameter can provide endless fun for people who like to tune their
|
||||
systems to the utmost degree. You can set all sorts of socket options which
|
||||
may make transfers faster (or slower!). Read the man page for the
|
||||
may make transfers faster (or slower!). Read the manpage for the
|
||||
**setsockopt()** system call for details on some of the options you may be
|
||||
able to set. By default no special socket options are set. These settings
|
||||
can also be specified via the `--sockopts` command-line option.
|
||||
@@ -136,7 +120,23 @@ a literal % into a value is to use %%.
|
||||
You can override the default backlog value when the daemon listens for
|
||||
connections. It defaults to 5.
|
||||
|
||||
# MODULE PARAMETERS
|
||||
You may also include any [MODULE PARAMETERS](#) in the global part of the
|
||||
config file, in which case the supplied value will override the default for
|
||||
that parameter.
|
||||
|
||||
You may use references to environment variables in the values of parameters.
|
||||
String parameters will have %VAR% references expanded as late as possible (when
|
||||
the string is first used in the program), allowing for the use of variables
|
||||
that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
|
||||
parameters (such as true/false settings) are expanded when read from the config
|
||||
file. If a variable does not exist in the environment, or if a sequence of
|
||||
characters is not a valid reference (such as an un-paired percent sign), the
|
||||
raw characters are passed through unchanged. This helps with backward
|
||||
compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
|
||||
string in a path could result in a very unsafe path). The safest way to insert
|
||||
a literal % into a value is to use %%.
|
||||
|
||||
## MODULE PARAMETERS
|
||||
|
||||
After the global parameters you should define a number of modules, each module
|
||||
exports a directory tree as a symbolic name. Modules are exported by specifying
|
||||
@@ -144,11 +144,17 @@ a module name in square brackets [module] followed by the parameters for that
|
||||
module. The module name cannot contain a slash or a closing square bracket.
|
||||
If the name contains whitespace, each internal sequence of whitespace will be
|
||||
changed into a single space, while leading or trailing whitespace will be
|
||||
discarded. Also, the name cannot be "global" as that exact name indicates that
|
||||
global parameters follow (see above).
|
||||
discarded.
|
||||
|
||||
As with GLOBAL PARAMETERS, you may use references to environment variables in
|
||||
the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
There is also a special module name of "[global]" that does not define a module
|
||||
but instead switches back to the global settings context where default
|
||||
parameters can be specified. Because each defined module gets its full set of
|
||||
parameters as a combination of the default values that are set at that position
|
||||
in the config file plus its own parameter list, the use of a "[global]" section
|
||||
can help to maintain shared config values for multiple modules.
|
||||
|
||||
As with [GLOBAL PARAMETERS](#), you may use references to environment variables
|
||||
in the values of parameters. See that section for details.
|
||||
|
||||
0. `comment`
|
||||
|
||||
@@ -162,6 +168,16 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
available in this module. You must specify this parameter for each module
|
||||
in `rsyncd.conf`.
|
||||
|
||||
If the value contains a "/./" element then the path will be divided at that
|
||||
point into a chroot dir and an inner-chroot subdir. If [`use chroot`](#)
|
||||
is set to false, though, the extraneous dot dir is just cleaned out of the
|
||||
path. An example of this idiom is:
|
||||
|
||||
> path = /var/rsync/./module1
|
||||
|
||||
This will (when chrooting) chroot to "/var/rsync" and set the inside-chroot
|
||||
path to "/module1".
|
||||
|
||||
You may base the path's value off of an environment variable by surrounding
|
||||
the variable name with percent signs. You can even reference a variable
|
||||
that is set by rsync when the user connects. For example, this would use
|
||||
@@ -177,7 +193,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
0. `use chroot`
|
||||
|
||||
If "use chroot" is true, the rsync daemon will chroot to the "path" before
|
||||
If "use chroot" is true, the rsync daemon will chroot to the "[path](#)" before
|
||||
starting the file transfer with the client. This has the advantage of
|
||||
extra protection against possible implementation security holes, but it has
|
||||
the disadvantages of requiring super-user privileges, of not being able to
|
||||
@@ -185,30 +201,48 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
path, and of complicating the preservation of users and groups by name (see
|
||||
below).
|
||||
|
||||
As an additional safety feature, you can specify a dot-dir in the module's
|
||||
"path" to indicate the point where the chroot should occur. This allows
|
||||
rsync to run in a chroot with a non-"/" path for the top of the transfer
|
||||
hierarchy. Doing this guards against unintended library loading (since
|
||||
those absolute paths will not be inside the transfer hierarchy unless you
|
||||
have used an unwise pathname), and lets you setup libraries for the chroot
|
||||
that are outside of the transfer. For example, specifying
|
||||
"/var/rsync/./module1" will chroot to the "/var/rsync" directory and set
|
||||
the inside-chroot path to "/module1". If you had omitted the dot-dir, the
|
||||
chroot would have used the whole path, and the inside-chroot path would
|
||||
have been "/".
|
||||
If `use chroot` is not set, it defaults to trying to enable a chroot but
|
||||
allows the daemon to continue (after logging a warning) if it fails. The
|
||||
one exception to this is when a module's [`path`](#) has a "/./" chroot
|
||||
divider in it -- this causes an unset value to be treated as true for that
|
||||
module.
|
||||
|
||||
When both "use chroot" and "daemon chroot" are false, OR the inside-chroot
|
||||
path of "use chroot" is not "/", rsync will: (1) munge symlinks by default
|
||||
for security reasons (see "munge symlinks" for a way to turn this off, but
|
||||
only if you trust your users), (2) substitute leading slashes in absolute
|
||||
paths with the module's path (so that options such as `--backup-dir`,
|
||||
`--compare-dest`, etc. interpret an absolute path as rooted in the module's
|
||||
"path" dir), and (3) trim ".." path elements from args if rsync believes
|
||||
they would escape the module hierarchy. The default for "use chroot" is
|
||||
true, and is the safer choice (especially if the module is not read-only).
|
||||
Prior to rsync 3.2.7, the default value was "true". The new "unset"
|
||||
default makes it easier to setup an rsync daemon as a non-root user or to
|
||||
run a daemon on a system where chroot fails. Explicitly setting the value
|
||||
to "true" in rsyncd.conf will always require the chroot to succeed.
|
||||
|
||||
When this parameter is enabled *and* the "name converter" parameter is
|
||||
*not* set, the "numeric ids" parameter will default to being enabled
|
||||
It is also possible to specify a dot-dir in the module's "[path](#)" to
|
||||
indicate that you want to chdir to the earlier part of the path and then
|
||||
serve files from inside the latter part of the path (with sanitizing and
|
||||
default symlink munging). This can be useful if you need some library dirs
|
||||
inside the chroot (typically for uid & gid lookups) but don't want to put
|
||||
the lib dir into the top of the served path (even though they can be hidden
|
||||
with an [`exclude`](#) directive). However, a better choice for a modern
|
||||
rsync setup is to use a [`name converter`](#)" and try to avoid inner lib
|
||||
dirs altogether. See also the [`daemon chroot`](#) parameter, which causes
|
||||
rsync to chroot into its own chroot area before doing any path-related
|
||||
chrooting.
|
||||
|
||||
If the daemon is serving the "/" dir (either directly or due to being
|
||||
chrooted to the module's path), rsync does not do any path sanitizing or
|
||||
(default) munging.
|
||||
|
||||
When it has to limit access to a particular subdir (either due to chroot
|
||||
being disabled or having an inside-chroot path set), rsync will munge
|
||||
symlinks (by default) and sanitize paths. Those that dislike munged
|
||||
symlinks (and really, really trust their users to not break out of the
|
||||
subdir) can disable the symlink munging via the "[munge symlinks](#)"
|
||||
parameter.
|
||||
|
||||
When rsync is sanitizing paths, it trims ".." path elements from args that
|
||||
it believes would escape the module hierarchy. It also substitutes leading
|
||||
slashes in absolute paths with the module's path (so that options such as
|
||||
`--backup-dir` & `--compare-dest` interpret an absolute path as rooted in
|
||||
the module's "[path](#)" dir).
|
||||
|
||||
When a chroot is in effect *and* the "[name converter](#)" parameter is
|
||||
*not* set, the "[numeric ids](#)" parameter will default to being enabled
|
||||
(disabling name lookups). This means that if you manually setup
|
||||
name-lookup libraries in your chroot (instead of using a name converter)
|
||||
that you need to explicitly set `numeric ids = false` for rsync to do name
|
||||
@@ -217,16 +251,16 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
If you copy library resources into the module's chroot area, you should
|
||||
protect them through your OS's normal user/group or ACL settings (to
|
||||
prevent the rsync module's user from being able to change them), and then
|
||||
hide them from the user's view via "exclude" (see how in the discussion of
|
||||
hide them from the user's view via "[exclude](#)" (see how in the discussion of
|
||||
that parameter). However, it's easier and safer to setup a name converter.
|
||||
|
||||
0. `daemon chroot`
|
||||
|
||||
This parameter specifies a path to which the daemon will chroot before
|
||||
beginning communication with clients. Module paths (and any "use chroot"
|
||||
beginning communication with clients. Module paths (and any "[use chroot](#)"
|
||||
settings) will then be related to this one. This lets you choose if you
|
||||
want the whole daemon to be chrooted (with this setting), just the
|
||||
transfers to be chrooted (with "use chroot"), or both. Keep in mind that
|
||||
transfers to be chrooted (with "[use chroot](#)"), or both. Keep in mind that
|
||||
the "daemon chroot" area may need various OS/lib/etc files installed to
|
||||
allow the daemon to function. By default the daemon runs without any
|
||||
chrooting.
|
||||
@@ -284,11 +318,11 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
transfer behave as if the client had passed the `--numeric-ids`
|
||||
command-line option. By default, this parameter is enabled for chroot
|
||||
modules and disabled for non-chroot modules. Also keep in mind that
|
||||
uid/gid preservation requires the module to be running as root (see "uid")
|
||||
or for "fake super" to be configured.
|
||||
uid/gid preservation requires the module to be running as root (see "[uid](#)")
|
||||
or for "[fake super](#)" to be configured.
|
||||
|
||||
A chroot-enabled module should not have this parameter set to false unless
|
||||
you're using a "name converter" program *or* you've taken steps to ensure
|
||||
you're using a "[name converter](#)" program *or* you've taken steps to ensure
|
||||
that the module has the necessary resources it needs to translate names and
|
||||
that it is not possible for a user to change those resources.
|
||||
|
||||
@@ -298,12 +332,12 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
(non-daemon-affecting) `--munge-links` command-line option (using a method
|
||||
described below). This should help protect your files from user trickery
|
||||
when your daemon module is writable. The default is disabled when
|
||||
"use chroot" is on with an inside-chroot path of "/", OR if "daemon chroot"
|
||||
"[use chroot](#)" is on with an inside-chroot path of "/", OR if "[daemon chroot](#)"
|
||||
is on, otherwise it is enabled.
|
||||
|
||||
If you disable this parameter on a daemon that is not read-only, there are
|
||||
tricks that a user can play with uploaded symlinks to access
|
||||
daemon-excluded items (if your module has any), and, if "use chroot" is
|
||||
daemon-excluded items (if your module has any), and, if "[use chroot](#)" is
|
||||
off, rsync can even be tricked into showing or changing data that is
|
||||
outside the module's path (as access-permissions allow).
|
||||
|
||||
@@ -324,7 +358,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
the source code named "munge-symlinks" that can be used to add or remove
|
||||
this prefix from your symlinks.
|
||||
|
||||
When this parameter is disabled on a writable module and "use chroot" is
|
||||
When this parameter is disabled on a writable module and "[use chroot](#)" is
|
||||
off (or the inside-chroot path is not "/"), incoming symlinks will be
|
||||
modified to drop a leading slash and to remove ".." path elements that
|
||||
rsync believes will allow a symlink to escape the module's hierarchy.
|
||||
@@ -340,10 +374,10 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
conversion in a chroot module without extra files in the chroot area, and
|
||||
also ensures that name-translation is done in a consistent manner. If the
|
||||
"charset" parameter is not set, the `--iconv` option is refused, just as if
|
||||
"iconv" had been specified via "refuse options".
|
||||
"iconv" had been specified via "[refuse options](#)".
|
||||
|
||||
If you wish to force users to always use `--iconv` for a particular module,
|
||||
add "no-iconv" to the "refuse options" parameter. Keep in mind that this
|
||||
add "no-iconv" to the "[refuse options](#)" parameter. Keep in mind that this
|
||||
will restrict access to your module to very new rsync clients.
|
||||
|
||||
0. `max connections`
|
||||
@@ -352,7 +386,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
connections you will allow. Any clients connecting when the maximum has
|
||||
been reached will receive a message telling them to try later. The default
|
||||
is 0, which means no limit. A negative value disables the module. See
|
||||
also the "lock file" parameter.
|
||||
also the "[lock file](#)" parameter.
|
||||
|
||||
0. `log file`
|
||||
|
||||
@@ -381,7 +415,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
facility name which is defined on your system. Common names are auth,
|
||||
authpriv, cron, daemon, ftp, kern, lpr, mail, news, security, syslog, user,
|
||||
uucp, local0, local1, local2, local3, local4, local5, local6 and local7.
|
||||
The default is daemon. This setting has no effect if the "log file"
|
||||
The default is daemon. This setting has no effect if the "[log file](#)"
|
||||
setting is a non-empty string (either set in the per-modules settings, or
|
||||
inherited from the global settings).
|
||||
|
||||
@@ -389,7 +423,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
This parameter allows you to specify the syslog tag to use when logging
|
||||
messages from the rsync daemon. The default is "rsyncd". This setting has
|
||||
no effect if the "log file" setting is a non-empty string (either set in
|
||||
no effect if the "[log file](#)" setting is a non-empty string (either set in
|
||||
the per-modules settings, or inherited from the global settings).
|
||||
|
||||
For example, if you wanted each authenticated user's name to be included in
|
||||
@@ -414,7 +448,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
0. `lock file`
|
||||
|
||||
This parameter specifies the file to use to support the "max connections"
|
||||
This parameter specifies the file to use to support the "[max connections](#)"
|
||||
parameter. The rsync daemon uses record locking on this file to ensure that
|
||||
the max connections limit is not exceeded for the modules sharing the lock
|
||||
file. The default is `/var/run/rsyncd.lock`.
|
||||
@@ -426,7 +460,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
"read only" is false then uploads will be possible if file permissions on
|
||||
the daemon side allow them. The default is for all modules to be read only.
|
||||
|
||||
Note that "auth users" can override this setting on a per-user basis.
|
||||
Note that "[auth users](#)" can override this setting on a per-user basis.
|
||||
|
||||
0. `write only`
|
||||
|
||||
@@ -460,8 +494,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
This parameter determines whether this module is listed when the client
|
||||
asks for a listing of available modules. In addition, if this is false,
|
||||
the daemon will pretend the module does not exist when a client denied by
|
||||
"hosts allow" or "hosts deny" attempts to access it. Realize that if
|
||||
"reverse lookup" is disabled globally but enabled for the module, the
|
||||
"[hosts allow](#)" or "[hosts deny](#)" attempts to access it. Realize that if
|
||||
"[reverse lookup](#)" is disabled globally but enabled for the module, the
|
||||
resulting reverse lookup to a potentially client-controlled DNS server may
|
||||
still reveal to the client that it hit an existing module. The default is
|
||||
for modules to be listable.
|
||||
@@ -470,10 +504,10 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
This parameter specifies the user name or user ID that file transfers to
|
||||
and from that module should take place as when the daemon was run as root.
|
||||
In combination with the "gid" parameter this determines what file
|
||||
In combination with the "[gid](#)" parameter this determines what file
|
||||
permissions are available. The default when run by a super-user is to
|
||||
switch to the system's "nobody" user. The default for a non-super-user is
|
||||
to not try to change the user. See also the "gid" parameter.
|
||||
to not try to change the user. See also the "[gid](#)" parameter.
|
||||
|
||||
The RSYNC_USER_NAME environment variable may be used to request that rsync
|
||||
run as the authorizing user. For example, if you want a rsync to run as
|
||||
@@ -489,7 +523,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
accessing the module. The first one will be the default group, and any
|
||||
extra ones be set as supplemental groups. You may also specify a "`*`" as
|
||||
the first gid in the list, which will be replaced by all the normal groups
|
||||
for the transfer's user (see "uid"). The default when run by a super-user
|
||||
for the transfer's user (see "[uid](#)"). The default when run by a super-user
|
||||
is to switch to your OS's "nobody" (or perhaps "nogroup") group with no
|
||||
other supplementary groups. The default for a non-super-user is to not
|
||||
change any group attributes (and indeed, your OS may not allow a
|
||||
@@ -505,13 +539,13 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
This parameter specifies a uid under which the daemon will run. The daemon
|
||||
usually runs as user root, and when this is left unset the user is left
|
||||
unchanged. See also the "uid" parameter.
|
||||
unchanged. See also the "[uid](#)" parameter.
|
||||
|
||||
0. `daemon gid`
|
||||
|
||||
This parameter specifies a gid under which the daemon will run. The daemon
|
||||
usually runs as group root, and when this is left unset, the group is left
|
||||
unchanged. See also the "gid" parameter.
|
||||
unchanged. See also the "[gid](#)" parameter.
|
||||
|
||||
0. `fake super`
|
||||
|
||||
@@ -532,8 +566,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
or tampering with private administrative files, such as files you may add
|
||||
to support uid/gid name translations.
|
||||
|
||||
The daemon filter chain is built from the "filter", "include from",
|
||||
"include", "exclude from", and "exclude" parameters, in that order of
|
||||
The daemon filter chain is built from the "filter", "[include from](#)",
|
||||
"[include](#)", "[exclude from](#)", and "[exclude](#)" parameters, in that order of
|
||||
priority. Anchored patterns are anchored at the root of the module. To
|
||||
prevent access to an entire subtree, for example, "`/secret`", you **must**
|
||||
exclude everything in the subtree; the easiest way to do this is with a
|
||||
@@ -560,8 +594,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
0. `include`
|
||||
|
||||
Use an "include" to override the effects of the "exclude" parameter. Only
|
||||
one "include" parameter can apply to a given module. See the "filter"
|
||||
Use an "include" to override the effects of the "[exclude](#)" parameter. Only
|
||||
one "include" parameter can apply to a given module. See the "[filter](#)"
|
||||
parameter for a description of how excluded files affect the daemon.
|
||||
|
||||
0. `exclude from`
|
||||
@@ -569,14 +603,14 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
This parameter specifies the name of a file on the daemon that contains
|
||||
daemon exclude patterns, one per line. Only one "exclude from" parameter
|
||||
can apply to a given module; if you have multiple exclude-from files, you
|
||||
can specify them as a merge file in the "filter" parameter. See the
|
||||
"filter" parameter for a description of how excluded files affect the
|
||||
can specify them as a merge file in the "[filter](#)" parameter. See the
|
||||
"[filter](#)" parameter for a description of how excluded files affect the
|
||||
daemon.
|
||||
|
||||
0. `include from`
|
||||
|
||||
Analogue of "exclude from" for a file of daemon include patterns. Only one
|
||||
"include from" parameter can apply to a given module. See the "filter"
|
||||
Analogue of "[exclude from](#)" for a file of daemon include patterns. Only one
|
||||
"include from" parameter can apply to a given module. See the "[filter](#)"
|
||||
parameter for a description of how excluded files affect the daemon.
|
||||
|
||||
0. `incoming chmod`
|
||||
@@ -611,7 +645,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
to supply a username and password to connect to the module. A challenge
|
||||
response authentication protocol is used for this exchange. The plain text
|
||||
usernames and passwords are stored in the file specified by the
|
||||
"secrets file" parameter. The default is for all users to be able to
|
||||
"[secrets file](#)" parameter. The default is for all users to be able to
|
||||
connect without a password (this is called "anonymous rsync").
|
||||
|
||||
In addition to username matching, you can specify groupname matching via a
|
||||
@@ -623,7 +657,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
Finally, options may be specified after a colon (:). The options allow you
|
||||
to "deny" a user or a group, set the access to "ro" (read-only), or set the
|
||||
access to "rw" (read/write). Setting an auth-rule-specific ro/rw setting
|
||||
overrides the module's "read only" setting.
|
||||
overrides the module's "[read only](#)" setting.
|
||||
|
||||
Be sure to put the rules in the order you want them to be matched, because
|
||||
the checking stops at the first matching user or group, and that is the
|
||||
@@ -661,7 +695,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
This parameter specifies the name of a file that contains the
|
||||
username:password and/or @groupname:password pairs used for authenticating
|
||||
this module. This file is only consulted if the "auth users" parameter is
|
||||
this module. This file is only consulted if the "[auth users](#)" parameter is
|
||||
specified. The file is line-based and contains one name:password pair per
|
||||
line. Any line has a hash (#) as the very first character on the line is
|
||||
considered a comment and is skipped. The passwords can contain any
|
||||
@@ -675,14 +709,14 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
"@groupname:password" line for the group that triggered the authentication.
|
||||
|
||||
It is up to you what kind of password entries you want to include, either
|
||||
users, groups, or both. The use of group rules in "auth users" does not
|
||||
users, groups, or both. The use of group rules in "[auth users](#)" does not
|
||||
require that you specify a group password if you do not want to use shared
|
||||
passwords.
|
||||
|
||||
There is no default for the "secrets file" parameter, you must choose a
|
||||
name (such as `/etc/rsyncd.secrets`). The file must normally not be
|
||||
readable by "other"; see "strict modes". If the file is not found or is
|
||||
rejected, no logins for a "user auth" module will be possible.
|
||||
readable by "other"; see "[strict modes](#)". If the file is not found or is
|
||||
rejected, no logins for an "[auth users](#)" module will be possible.
|
||||
|
||||
0. `strict modes`
|
||||
|
||||
@@ -714,11 +748,11 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
addresses which match the masked IP address will be allowed in.
|
||||
- a hostname pattern using wildcards. If the hostname of the connecting IP
|
||||
(as determined by a reverse lookup) matches the wildcarded name (using
|
||||
the same rules as normal unix filename matching), the client is allowed
|
||||
in. This only works if "reverse lookup" is enabled (the default).
|
||||
the same rules as normal Unix filename matching), the client is allowed
|
||||
in. This only works if "[reverse lookup](#)" is enabled (the default).
|
||||
- a hostname. A plain hostname is matched against the reverse DNS of the
|
||||
connecting IP (if "reverse lookup" is enabled), and/or the IP of the
|
||||
given hostname is matched against the connecting IP (if "forward lookup"
|
||||
connecting IP (if "[reverse lookup](#)" is enabled), and/or the IP of the
|
||||
given hostname is matched against the connecting IP (if "[forward lookup](#)"
|
||||
is enabled, as it is by default). Any match will be allowed in.
|
||||
- an '@' followed by a netgroup name, which will match if the reverse DNS
|
||||
of the connecting IP is in the specified netgroup.
|
||||
@@ -730,11 +764,11 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
> fe80::%link1/64
|
||||
> fe80::%link1/ffff:ffff:ffff:ffff::
|
||||
|
||||
You can also combine "hosts allow" with "hosts deny" as a way to add
|
||||
You can also combine "hosts allow" with "[hosts deny](#)" as a way to add
|
||||
exceptions to your deny list. When both parameters are specified, the
|
||||
"hosts allow" parameter is checked first and a match results in the client
|
||||
being able to connect. A non-allowed host is then matched against the
|
||||
"hosts deny" list to see if it should be rejected. A host that does not
|
||||
"[hosts deny](#)" list to see if it should be rejected. A host that does not
|
||||
match either list is allowed to connect.
|
||||
|
||||
The default is no "hosts allow" parameter, which means all hosts can
|
||||
@@ -745,7 +779,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
This parameter allows you to specify a list of comma- and/or
|
||||
whitespace-separated patterns that are matched against a connecting clients
|
||||
hostname and IP address. If the pattern matches then the connection is
|
||||
rejected. See the "hosts allow" parameter for more information.
|
||||
rejected. See the "[hosts allow](#)" parameter for more information.
|
||||
|
||||
The default is no "hosts deny" parameter, which means all hosts can
|
||||
connect.
|
||||
@@ -753,8 +787,8 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
0. `reverse lookup`
|
||||
|
||||
Controls whether the daemon performs a reverse lookup on the client's IP
|
||||
address to determine its hostname, which is used for "hosts allow" &
|
||||
"hosts deny" checks and the "%h" log escape. This is enabled by default,
|
||||
address to determine its hostname, which is used for "[hosts allow](#)" &
|
||||
"[hosts deny](#)" checks and the "%h" log escape. This is enabled by default,
|
||||
but you may wish to disable it to save time if you know the lookup will not
|
||||
return a useful result, in which case the daemon will use the name
|
||||
"UNDETERMINED" instead.
|
||||
@@ -794,7 +828,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
logs the transfer at the end, so if a transfer is aborted, no mention will
|
||||
be made in the log file.
|
||||
|
||||
If you want to customize the log lines, see the "log format" parameter.
|
||||
If you want to customize the log lines, see the "[log format](#)" parameter.
|
||||
|
||||
0. `log format`
|
||||
|
||||
@@ -811,7 +845,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
(e.g. "`%''l %'b %f`").
|
||||
|
||||
The default log format is "`%o %h [%a] %m (%u) %f %l`", and a "`%t [%p] `"
|
||||
is always prefixed when using the "log file" parameter. (A perl script
|
||||
is always prefixed when using the "[log file](#)" parameter. (A perl script
|
||||
that will summarize this default log format is included in the rsync source
|
||||
code distribution in the "support" subdirectory: rsyncstats.)
|
||||
|
||||
@@ -892,7 +926,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
> refuse options = * !a !v !compress*
|
||||
|
||||
Don't worry that the "`*`" will refuse certain vital options such as
|
||||
`--dry-run`, `--server`, `--no-iconv`, `--protect-args`, etc. These
|
||||
`--dry-run`, `--server`, `--no-iconv`, `--seclude-args`, etc. These
|
||||
important options are not matched by wild-card, so they must be overridden
|
||||
by their exact name. For instance, if you're forcing iconv transfers you
|
||||
could use something like this:
|
||||
@@ -922,7 +956,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
|
||||
> refuse options = * !a !delete* delete-after
|
||||
|
||||
A note on refusing "compress": it may be better to set the "dont compress"
|
||||
A note on refusing "compress": it may be better to set the "[dont compress](#)"
|
||||
daemon parameter to "`*`" and ensure that `RSYNC_COMPRESS_LIST=zlib` is set
|
||||
in the environment of the daemon in order to disable compression silently
|
||||
instead of returning an error that forces the client to remove the `-z`
|
||||
@@ -931,9 +965,10 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
If you are un-refusing the compress option, you may want to match
|
||||
"`!compress*`" if you also want to allow the `--compress-level` option.
|
||||
|
||||
Note that the "write-devices" option is refused by default, but can be
|
||||
explicitly accepted with "`!write-devices`". The options "log-file" and
|
||||
"log-file-format" are forcibly refused and cannot be accepted.
|
||||
Note that the "copy-devices" & "write-devices" options are refused by
|
||||
default, but they can be explicitly accepted with "`!copy-devices`" and/or
|
||||
"`!write-devices`". The options "log-file" and "log-file-format" are
|
||||
forcibly refused and cannot be accepted.
|
||||
|
||||
Here are all the options that are not matched by wild-cards:
|
||||
|
||||
@@ -943,12 +978,12 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
receiver. While rsync passes the older alias `--log-format` for
|
||||
compatibility reasons, this options should not be confused with
|
||||
`--log-file-format`.
|
||||
- `--sender`: Use "write only" parameter instead of refusing this.
|
||||
- `--sender`: Use "[write only](#)" parameter instead of refusing this.
|
||||
- `--dry-run`, `-n`: Who would want to disable this?
|
||||
- `--protect-args`, `-s`: This actually makes transfers safer.
|
||||
- `--seclude-args`, `-s`: Is the oldest arg-protection method.
|
||||
- `--from0`, `-0`: Makes it easier to accept/refuse `--files-from` without
|
||||
affecting this helpful modifier.
|
||||
- `--iconv`: This is auto-disabled based on "charset" parameter.
|
||||
- `--iconv`: This is auto-disabled based on "[charset](#)" parameter.
|
||||
- `--no-iconv`: Most transfers use this option.
|
||||
- `--checksum-seed`: Is a fairly rare, safe option.
|
||||
- `--write-devices`: Is non-wild but also auto-disabled.
|
||||
@@ -988,7 +1023,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
_not_ displayed if the script returns success. The other programs cannot
|
||||
send any text to the user. All output except for the `pre-xfer exec`
|
||||
stdout goes to the corresponding daemon's stdout/stderr, which is typically
|
||||
discarded. See the `--no-detatch` option for a way to see the daemon's
|
||||
discarded. See the `--no-detach` option for a way to see the daemon's
|
||||
output, which can assist with debugging.
|
||||
|
||||
Note that the `early exec` command runs before any part of the transfer
|
||||
@@ -1038,7 +1073,17 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
**system()** call's default shell), and use RSYNC_NO_XFER_EXEC to disable
|
||||
both options completely.
|
||||
|
||||
# CONFIG DIRECTIVES
|
||||
0. `temp dir`
|
||||
|
||||
Specifies a directory that rsync should use for temporary files created
|
||||
during the transfer of updated files. If that directory is on a different
|
||||
partition, after transfer file is being copied instead of unlinked.
|
||||
|
||||
This parameter equals with `--temp-dir` option, so please consult rsync
|
||||
manpage for further information.
|
||||
|
||||
|
||||
## CONFIG DIRECTIVES
|
||||
|
||||
There are currently two config directives available that allow a config file to
|
||||
incorporate the contents of other files: `&include` and `&merge`. Both allow
|
||||
@@ -1093,7 +1138,7 @@ This would merge any `/etc/rsyncd.d/*.inc` files (for global values that should
|
||||
stay in effect), and then include any `/etc/rsyncd.d/*.conf` files (defining
|
||||
modules without any global-value cross-talk).
|
||||
|
||||
# AUTHENTICATION STRENGTH
|
||||
## AUTHENTICATION STRENGTH
|
||||
|
||||
The authentication protocol used in rsync is a 128 bit MD4 based challenge
|
||||
response system. This is fairly weak protection, though (with at least one
|
||||
@@ -1108,18 +1153,18 @@ authentication is provided. Use ssh as the transport if you want encryption.
|
||||
You can also make use of SSL/TLS encryption if you put rsync behind an
|
||||
SSL proxy.
|
||||
|
||||
# SSL/TLS Daemon Setup
|
||||
## SSL/TLS Daemon Setup
|
||||
|
||||
When setting up an rsync daemon for access via SSL/TLS, you will need to
|
||||
configure a proxy (such as haproxy or nginx) as the front-end that handles the
|
||||
encryption.
|
||||
configure a TCP proxy (such as haproxy or nginx) as the front-end that handles
|
||||
the encryption.
|
||||
|
||||
- You should limit the access to the backend-rsyncd port to only allow the
|
||||
proxy to connect. If it is on the same host as the proxy, then configuring
|
||||
it to only listen on localhost is a good idea.
|
||||
- You should consider turning on the `proxy protocol` parameter if your proxy
|
||||
supports sending that information. The examples below assume that this is
|
||||
enabled.
|
||||
- You should consider turning on the `proxy protocol` rsync-daemon parameter if
|
||||
your proxy supports sending that information. The examples below assume that
|
||||
this is enabled.
|
||||
|
||||
An example haproxy setup is as follows:
|
||||
|
||||
@@ -1146,14 +1191,17 @@ An example nginx proxy setup is as follows:
|
||||
> ssl_certificate_key /etc/letsencrypt/example.com/privkey.pem;
|
||||
>
|
||||
> proxy_pass localhost:873;
|
||||
> proxy_protocol on; # Requires "proxy protocol = true"
|
||||
> proxy_protocol on; # Requires rsyncd.conf "proxy protocol = true"
|
||||
> proxy_timeout 1m;
|
||||
> proxy_connect_timeout 5s;
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
|
||||
# EXAMPLES
|
||||
If rsyncd should be accessible encrypted and unencrypted at the same time make
|
||||
the proxy listen on port 873 as well and let it handle both streams.
|
||||
|
||||
## DAEMON CONFIG EXAMPLES
|
||||
|
||||
A simple rsyncd.conf file that allow anonymous rsync to a ftp area at
|
||||
`/home/ftp` would be:
|
||||
@@ -1202,46 +1250,40 @@ The /etc/rsyncd.secrets file would look something like this:
|
||||
> tridge:mypass
|
||||
> susan:herpass
|
||||
|
||||
# FILES
|
||||
## FILES
|
||||
|
||||
/etc/rsyncd.conf or rsyncd.conf
|
||||
|
||||
# SEE ALSO
|
||||
## SEE ALSO
|
||||
|
||||
**rsync**(1), **rsync-ssl**(1)
|
||||
[**rsync**(1)](rsync.1), [**rsync-ssl**(1)](rsync-ssl.1)
|
||||
|
||||
# BUGS
|
||||
## BUGS
|
||||
|
||||
Please report bugs! The rsync bug tracking system is online at
|
||||
<https://rsync.samba.org/>.
|
||||
|
||||
# VERSION
|
||||
## VERSION
|
||||
|
||||
This man page is current for version @VERSION@ of rsync.
|
||||
This manpage is current for version @VERSION@ of rsync.
|
||||
|
||||
# CREDITS
|
||||
## CREDITS
|
||||
|
||||
rsync is distributed under the GNU General Public License. See the file
|
||||
COPYING for details.
|
||||
Rsync is distributed under the GNU General Public License. See the file
|
||||
[COPYING](COPYING) for details.
|
||||
|
||||
The primary ftp site for rsync is <ftp://rsync.samba.org/pub/rsync>
|
||||
An rsync web site is available at <https://rsync.samba.org/> and its github
|
||||
project is <https://github.com/RsyncProject/rsync>.
|
||||
|
||||
A web site is available at <https://rsync.samba.org/>.
|
||||
|
||||
We would be delighted to hear from you if you like this program.
|
||||
|
||||
This program uses the zlib compression library written by Jean-loup Gailly and
|
||||
Mark Adler.
|
||||
|
||||
# THANKS
|
||||
## THANKS
|
||||
|
||||
Thanks to Warren Stanley for his original idea and patch for the rsync daemon.
|
||||
Thanks to Karsten Thygesen for his many suggestions and documentation!
|
||||
|
||||
# AUTHOR
|
||||
## AUTHOR
|
||||
|
||||
rsync was written by Andrew Tridgell and Paul Mackerras. Many people have
|
||||
later contributed to it.
|
||||
Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
|
||||
people from around the world have helped to maintain and improve it.
|
||||
|
||||
Mailing lists for support and development are available at
|
||||
<https://lists.samba.org/>.
|
||||
Executable
+485
@@ -0,0 +1,485 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
|
||||
# Copyright (C) 2003-2022 Wayne Davison
|
||||
# Copyright (C) 2026 Andrew Tridgell
|
||||
#
|
||||
# Rewrite of runtests.sh in Python (runtests.sh is now deprecated).
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version
|
||||
# 2 as published by the Free Software Foundation.
|
||||
|
||||
"""rsync test runner.
|
||||
|
||||
Invokes test scripts from testsuite/ and reports results.
|
||||
Can be called by 'make check' or directly.
|
||||
|
||||
Usage:
|
||||
./runtests.py [options] [TEST ...]
|
||||
|
||||
Each TEST is a test name (e.g. 'delete') or glob pattern (e.g. 'xattr*').
|
||||
If no tests are specified, all tests are run.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import concurrent.futures
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
|
||||
def parse_args():
|
||||
p = argparse.ArgumentParser(description='Run rsync test suite')
|
||||
p.add_argument('tests', nargs='*', metavar='TEST',
|
||||
help='Test names or patterns to run (default: all)')
|
||||
p.add_argument('-j', '--parallel', type=int, default=1, metavar='N',
|
||||
help='Run up to N tests in parallel (default: 1)')
|
||||
p.add_argument('--valgrind', action='store_true',
|
||||
help='Run rsync under valgrind (logs to per-process files)')
|
||||
p.add_argument('--valgrind-opts', default='', metavar='OPTS',
|
||||
help='Extra valgrind options (e.g. "--leak-check=full")')
|
||||
p.add_argument('--preserve-scratch', action='store_true',
|
||||
help='Keep scratch directories after tests complete')
|
||||
p.add_argument('--log-level', type=int, default=1, metavar='N',
|
||||
help='Verbosity level 1-10 (default: 1)')
|
||||
p.add_argument('--always-log', action='store_true',
|
||||
help='Show test logs even for passing tests')
|
||||
p.add_argument('--stop-on-fail', action='store_true',
|
||||
help='Stop after first test failure')
|
||||
p.add_argument('--timeout', type=int, default=300, metavar='SECS',
|
||||
help='Per-test timeout in seconds (default: 300)')
|
||||
p.add_argument('--rsync-bin', default=None, metavar='PATH',
|
||||
help='Path to rsync binary (default: ./rsync)')
|
||||
p.add_argument('--tooldir', default=None, metavar='DIR',
|
||||
help='Tool/build directory (default: cwd)')
|
||||
p.add_argument('--srcdir', default=None, metavar='DIR',
|
||||
help='Source directory (default: script directory)')
|
||||
p.add_argument('--protocol', type=int, default=None, metavar='VER',
|
||||
help='Force protocol version (adds --protocol=VER to rsync)')
|
||||
p.add_argument('--expect-skipped', default=None, metavar='LIST',
|
||||
help='Comma-separated list of expected-skipped tests')
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def find_setfacl_nodef(scratchbase):
|
||||
"""Determine the setfacl command to remove default ACLs."""
|
||||
for cmd in [
|
||||
['setacl', '-k', 'u::7,g::5,o:5', scratchbase],
|
||||
['setfacl', '-k', scratchbase],
|
||||
['setfacl', '-s', 'u::7,g::5,o:5', scratchbase],
|
||||
]:
|
||||
try:
|
||||
subprocess.run(cmd, capture_output=True, timeout=5)
|
||||
return cmd[:2] if cmd[0] == 'setacl' else cmd[:2]
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
continue
|
||||
try:
|
||||
r = subprocess.run(['setfacl', '--help'], capture_output=True, text=True, timeout=5)
|
||||
if '-k,' in r.stdout or '-k,' in r.stderr:
|
||||
return ['setfacl', '-k']
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def get_tls_args(config_h):
|
||||
"""Determine TLS_ARGS from config.h."""
|
||||
args = ''
|
||||
try:
|
||||
with open(config_h) as f:
|
||||
text = f.read()
|
||||
if '#define HAVE_LUTIMES 1' in text:
|
||||
args += ' -l'
|
||||
if '#undef CHOWN_MODIFIES_SYMLINK' in text:
|
||||
args += ' -L'
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return args.strip()
|
||||
|
||||
|
||||
def read_shconfig(path):
|
||||
"""Read shell config variables from shconfig."""
|
||||
env = {}
|
||||
try:
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith('#') or line.startswith('export') or not line:
|
||||
continue
|
||||
if '=' in line:
|
||||
k, _, v = line.partition('=')
|
||||
env[k.strip()] = v.strip().strip('"')
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return env
|
||||
|
||||
|
||||
def get_testuser():
|
||||
"""Determine the current test user."""
|
||||
for cmd in ['/usr/bin/whoami', '/usr/ucb/whoami', '/bin/whoami']:
|
||||
if os.path.isfile(cmd):
|
||||
try:
|
||||
return subprocess.check_output([cmd], text=True).strip()
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
try:
|
||||
return subprocess.check_output(['id', '-un'], text=True).strip()
|
||||
except (FileNotFoundError, subprocess.CalledProcessError):
|
||||
return os.environ.get('LOGNAME', os.environ.get('USER', 'UNKNOWN'))
|
||||
|
||||
|
||||
def prep_scratch(scratchdir, srcdir, tooldir, setfacl_nodef):
|
||||
"""Prepare a scratch directory for a test."""
|
||||
if os.path.isdir(scratchdir):
|
||||
subprocess.run(['chmod', '-R', 'u+rwX', scratchdir], capture_output=True)
|
||||
subprocess.run(['rm', '-rf', scratchdir], capture_output=True)
|
||||
os.makedirs(scratchdir, exist_ok=True)
|
||||
if setfacl_nodef:
|
||||
subprocess.run(setfacl_nodef + [scratchdir], capture_output=True)
|
||||
try:
|
||||
os.chmod(scratchdir, os.stat(scratchdir).st_mode & ~0o2000) # clear setgid
|
||||
except OSError:
|
||||
pass
|
||||
src_link = os.path.join(scratchdir, 'src')
|
||||
if not os.path.exists(src_link):
|
||||
if os.path.isabs(srcdir):
|
||||
os.symlink(srcdir, src_link)
|
||||
else:
|
||||
os.symlink(os.path.join(tooldir, srcdir), src_link)
|
||||
|
||||
|
||||
def collect_tests(suitedir, patterns):
|
||||
"""Collect test scripts matching the given patterns."""
|
||||
if not patterns:
|
||||
tests = sorted(glob.glob(os.path.join(suitedir, '*.test')))
|
||||
else:
|
||||
tests = []
|
||||
for pat in patterns:
|
||||
if not pat.endswith('.test'):
|
||||
pat = pat + '.test'
|
||||
matches = sorted(glob.glob(os.path.join(suitedir, pat)))
|
||||
tests.extend(matches)
|
||||
return tests
|
||||
|
||||
|
||||
def build_rsync_cmd(rsync_bin, args, scratchbase):
|
||||
"""Build the RSYNC command string for tests."""
|
||||
parts = []
|
||||
if args.valgrind:
|
||||
vlog = os.path.join(scratchbase, 'valgrind.%p.log')
|
||||
vopts = f'--log-file={vlog}'
|
||||
if args.valgrind_opts:
|
||||
vopts += ' ' + args.valgrind_opts
|
||||
parts.append(f'valgrind {vopts}')
|
||||
parts.append(rsync_bin)
|
||||
if args.protocol is not None:
|
||||
parts.append(f'--protocol={args.protocol}')
|
||||
return ' '.join(parts)
|
||||
|
||||
|
||||
class TestResult:
|
||||
"""Result of a single test execution."""
|
||||
__slots__ = ('testbase', 'result', 'output', 'skipped_reason')
|
||||
|
||||
def __init__(self, testbase, result, output='', skipped_reason=''):
|
||||
self.testbase = testbase
|
||||
self.result = result
|
||||
self.output = output
|
||||
self.skipped_reason = skipped_reason
|
||||
|
||||
|
||||
def run_one_test(testscript, testbase, scratchdir, base_env, timeout,
|
||||
srcdir, tooldir, setfacl_nodef, always_log):
|
||||
"""Run a single test. Returns a TestResult.
|
||||
|
||||
This function is safe to call from multiple threads — it uses only
|
||||
per-test state (unique scratchdir, copy of env).
|
||||
"""
|
||||
prep_scratch(scratchdir, srcdir, tooldir, setfacl_nodef)
|
||||
|
||||
env = base_env.copy()
|
||||
env['scratchdir'] = scratchdir
|
||||
|
||||
logfile = os.path.join(scratchdir, 'test.log')
|
||||
try:
|
||||
with open(logfile, 'w') as log:
|
||||
proc = subprocess.run(
|
||||
['sh', '-e', testscript],
|
||||
stdout=log, stderr=subprocess.STDOUT,
|
||||
env=env, timeout=timeout,
|
||||
cwd=env.get('TOOLDIR', '.')
|
||||
)
|
||||
result = proc.returncode
|
||||
except subprocess.TimeoutExpired:
|
||||
result = 1
|
||||
with open(logfile, 'a') as log:
|
||||
log.write(f"\nTIMEOUT: test took over {timeout} seconds\n")
|
||||
|
||||
# Build output text
|
||||
output_parts = []
|
||||
|
||||
show_log = always_log or (result not in (0, 77, 78))
|
||||
if show_log:
|
||||
output_parts.append(f'----- {testbase} log follows')
|
||||
try:
|
||||
with open(logfile) as f:
|
||||
output_parts.append(f.read().rstrip())
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
output_parts.append(f'----- {testbase} log ends')
|
||||
rsyncd_log = os.path.join(scratchdir, 'rsyncd.log')
|
||||
if os.path.isfile(rsyncd_log):
|
||||
output_parts.append(f'----- {testbase} rsyncd.log follows')
|
||||
with open(rsyncd_log) as f:
|
||||
output_parts.append(f.read().rstrip())
|
||||
output_parts.append(f'----- {testbase} rsyncd.log ends')
|
||||
|
||||
skipped_reason = ''
|
||||
if result == 0:
|
||||
output_parts.append(f'PASS {testbase}')
|
||||
elif result == 77:
|
||||
whyfile = os.path.join(scratchdir, 'whyskipped')
|
||||
try:
|
||||
with open(whyfile) as f:
|
||||
skipped_reason = f.read().strip()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
output_parts.append(f'SKIP {testbase} ({skipped_reason})')
|
||||
elif result == 78:
|
||||
output_parts.append(f'XFAIL {testbase}')
|
||||
else:
|
||||
output_parts.append(f'FAIL {testbase}')
|
||||
|
||||
return TestResult(testbase, result, '\n'.join(output_parts), skipped_reason)
|
||||
|
||||
|
||||
# Lock for serializing output in parallel mode
|
||||
_print_lock = threading.Lock()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
|
||||
# Also accept legacy environment variables
|
||||
if args.preserve_scratch or os.environ.get('preserve_scratch') == 'yes':
|
||||
args.preserve_scratch = True
|
||||
if args.log_level == 1:
|
||||
args.log_level = int(os.environ.get('loglevel', '1'))
|
||||
if args.expect_skipped is None:
|
||||
args.expect_skipped = os.environ.get('RSYNC_EXPECT_SKIPPED', 'IGNORE')
|
||||
if os.environ.get('whichtests'):
|
||||
args.tests = [os.environ['whichtests']]
|
||||
|
||||
# Determine directories
|
||||
tooldir = args.tooldir or os.environ.get('TOOLDIR') or os.getcwd()
|
||||
script_path = os.path.dirname(os.path.abspath(__file__))
|
||||
srcdir = args.srcdir or script_path
|
||||
if not srcdir or srcdir == '.':
|
||||
srcdir = tooldir
|
||||
rsync_bin = args.rsync_bin or os.environ.get('rsync_bin') or os.path.join(tooldir, 'rsync')
|
||||
|
||||
suitedir = os.path.join(srcdir, 'testsuite')
|
||||
scratchbase = os.path.join(os.environ.get('scratchbase', tooldir), 'testtmp')
|
||||
os.makedirs(scratchbase, exist_ok=True)
|
||||
|
||||
shconfig = read_shconfig(os.path.join(tooldir, 'shconfig'))
|
||||
tls_args = get_tls_args(os.path.join(tooldir, 'config.h'))
|
||||
setfacl_nodef = find_setfacl_nodef(scratchbase)
|
||||
rsync_cmd = build_rsync_cmd(rsync_bin, args, scratchbase)
|
||||
|
||||
if not os.path.isfile(rsync_bin):
|
||||
sys.stderr.write(f"rsync_bin {rsync_bin} is not a file\n")
|
||||
sys.exit(2)
|
||||
if not os.path.isdir(srcdir):
|
||||
sys.stderr.write(f"srcdir {srcdir} is not a directory\n")
|
||||
sys.exit(2)
|
||||
|
||||
# Helper programs the test scripts invoke directly. Missing any of these
|
||||
# would cause many tests to fail with confusing "not found" errors, so
|
||||
# check up front and point the user at the make target that builds them.
|
||||
required_helpers = ['tls', 'trimslash', 't_unsafe', 't_chmod_secure',
|
||||
't_secure_relpath',
|
||||
'wildtest', 'getgroups', 'getfsdev']
|
||||
missing = [h for h in required_helpers
|
||||
if not os.path.isfile(os.path.join(tooldir, h))]
|
||||
if missing:
|
||||
sys.stderr.write(
|
||||
f"runtests.py: missing test helper program(s) in {tooldir}: "
|
||||
f"{', '.join(missing)}\n"
|
||||
f"Build them with: make {' '.join(missing)}\n"
|
||||
f"or run the full test target: make check\n"
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
testuser = get_testuser()
|
||||
|
||||
# Print header
|
||||
print('=' * 60)
|
||||
print(f'{sys.argv[0]} running in {tooldir}')
|
||||
print(f' rsync_bin={rsync_cmd}')
|
||||
print(f' srcdir={srcdir}')
|
||||
print(f' TLS_ARGS={tls_args}')
|
||||
print(f' testuser={testuser}')
|
||||
print(f' os={subprocess.check_output(["uname", "-a"], text=True).strip()}')
|
||||
print(f' preserve_scratch={"yes" if args.preserve_scratch else "no"}')
|
||||
if args.valgrind:
|
||||
print(f' valgrind=enabled (logs in valgrind.*.log)')
|
||||
if args.parallel > 1:
|
||||
print(f' parallel={args.parallel}')
|
||||
print(f' scratchbase={scratchbase}')
|
||||
|
||||
# Build base environment for test scripts
|
||||
path = os.environ.get('PATH', '')
|
||||
if os.path.isdir('/usr/xpg4/bin'):
|
||||
path = '/usr/xpg4/bin:' + path
|
||||
|
||||
base_env = os.environ.copy()
|
||||
base_env.update({
|
||||
'PATH': path,
|
||||
'POSIXLY_CORRECT': '1',
|
||||
'TOOLDIR': tooldir,
|
||||
'srcdir': srcdir,
|
||||
'RSYNC': rsync_cmd,
|
||||
'TLS_ARGS': tls_args,
|
||||
'RUNSHFLAGS': '-e',
|
||||
'scratchbase': scratchbase,
|
||||
'suitedir': suitedir,
|
||||
'TESTRUN_TIMEOUT': str(args.timeout),
|
||||
'HOME': scratchbase,
|
||||
})
|
||||
for k, v in shconfig.items():
|
||||
if v:
|
||||
base_env[k] = v
|
||||
if setfacl_nodef:
|
||||
base_env['setfacl_nodef'] = ' '.join(setfacl_nodef)
|
||||
else:
|
||||
base_env['setfacl_nodef'] = 'true'
|
||||
if args.log_level > 8:
|
||||
base_env['RUNSHFLAGS'] = '-e -x'
|
||||
|
||||
# Collect tests
|
||||
tests = collect_tests(suitedir, args.tests)
|
||||
full_run = len(args.tests) == 0
|
||||
|
||||
# Record test order for consistent skipped-list output
|
||||
test_order = {os.path.basename(t).replace('.test', ''): i for i, t in enumerate(tests)}
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
skipped = 0
|
||||
skipped_list = []
|
||||
|
||||
def process_result(tr):
|
||||
"""Process a TestResult and update counters. Returns True if test failed."""
|
||||
nonlocal passed, failed, skipped
|
||||
with _print_lock:
|
||||
if tr.output:
|
||||
print(tr.output)
|
||||
scratchdir = os.path.join(scratchbase, tr.testbase)
|
||||
if tr.result == 0:
|
||||
passed += 1
|
||||
if not args.preserve_scratch and os.path.isdir(scratchdir):
|
||||
subprocess.run(['rm', '-rf', scratchdir], capture_output=True)
|
||||
return False
|
||||
elif tr.result == 77:
|
||||
skipped_list.append(tr.testbase)
|
||||
skipped += 1
|
||||
if not args.preserve_scratch and os.path.isdir(scratchdir):
|
||||
subprocess.run(['rm', '-rf', scratchdir], capture_output=True)
|
||||
return False
|
||||
elif tr.result == 78:
|
||||
failed += 1
|
||||
return True
|
||||
else:
|
||||
failed += 1
|
||||
return True
|
||||
|
||||
if args.parallel > 1:
|
||||
# Parallel execution
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=args.parallel) as executor:
|
||||
futures = {}
|
||||
for testscript in tests:
|
||||
testbase = os.path.basename(testscript).replace('.test', '')
|
||||
scratchdir = os.path.join(scratchbase, testbase)
|
||||
timeout = 600 if 'hardlinks' in testbase else args.timeout
|
||||
f = executor.submit(
|
||||
run_one_test, testscript, testbase, scratchdir,
|
||||
base_env, timeout, srcdir, tooldir, setfacl_nodef,
|
||||
args.always_log
|
||||
)
|
||||
futures[f] = testbase
|
||||
|
||||
for f in concurrent.futures.as_completed(futures):
|
||||
tr = f.result()
|
||||
is_fail = process_result(tr)
|
||||
if is_fail and args.stop_on_fail:
|
||||
# Cancel pending futures
|
||||
for pending in futures:
|
||||
pending.cancel()
|
||||
break
|
||||
else:
|
||||
# Sequential execution
|
||||
for testscript in tests:
|
||||
testbase = os.path.basename(testscript).replace('.test', '')
|
||||
scratchdir = os.path.join(scratchbase, testbase)
|
||||
timeout = 600 if 'hardlinks' in testbase else args.timeout
|
||||
tr = run_one_test(
|
||||
testscript, testbase, scratchdir,
|
||||
base_env, timeout, srcdir, tooldir, setfacl_nodef,
|
||||
args.always_log
|
||||
)
|
||||
is_fail = process_result(tr)
|
||||
if is_fail and args.stop_on_fail:
|
||||
break
|
||||
|
||||
# Check valgrind logs for errors
|
||||
vg_errors = 0
|
||||
if args.valgrind:
|
||||
for vlog in sorted(glob.glob(os.path.join(scratchbase, 'valgrind.*.log'))):
|
||||
try:
|
||||
with open(vlog) as f:
|
||||
content = f.read()
|
||||
for line in content.splitlines():
|
||||
if 'ERROR SUMMARY:' in line and 'ERROR SUMMARY: 0 errors' not in line:
|
||||
vg_errors += 1
|
||||
print(f'----- valgrind errors in {os.path.basename(vlog)}:')
|
||||
print(content)
|
||||
break
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Summary
|
||||
print('-' * 60)
|
||||
print('----- overall results:')
|
||||
print(f' {passed} passed')
|
||||
if failed > 0:
|
||||
print(f' {failed} failed')
|
||||
if skipped > 0:
|
||||
print(f' {skipped} skipped')
|
||||
if vg_errors > 0:
|
||||
print(f' {vg_errors} valgrind error(s) found (see logs in {scratchbase})')
|
||||
|
||||
skipped_str = ','.join(sorted(skipped_list, key=lambda x: test_order.get(x, 0)))
|
||||
if full_run and args.expect_skipped != 'IGNORE':
|
||||
print('----- skipped results:')
|
||||
print(f' expected: {args.expect_skipped}')
|
||||
print(f' got: {skipped_str}')
|
||||
else:
|
||||
skipped_str = ''
|
||||
args.expect_skipped = ''
|
||||
|
||||
print('-' * 60)
|
||||
|
||||
exit_code = failed + vg_errors
|
||||
if exit_code == 0 and skipped_str != args.expect_skipped:
|
||||
exit_code = 1
|
||||
|
||||
print(f'overall result is {exit_code}')
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loaded 100 of 185 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user