mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-06-02 19:28:32 -04:00
Compare commits
265 Commits
v3.2.4pre1
...
v3.2.7-sec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ab4c3de6f | ||
|
|
d094c4723a | ||
|
|
36860669cc | ||
|
|
0beceb95ac | ||
|
|
4370cb2ce1 | ||
|
|
f406c780ec | ||
|
|
259e04f923 | ||
|
|
189612f94a | ||
|
|
9354f27f9d | ||
|
|
a335091e91 | ||
|
|
3ac8349e07 | ||
|
|
816aa32059 | ||
|
|
f50c1d59c4 | ||
|
|
74ea276900 | ||
|
|
0a5fa00fdc | ||
|
|
4255413e0c | ||
|
|
901041dddc | ||
|
|
7c8a647c2e | ||
|
|
a277a06b10 | ||
|
|
39b3074a1a | ||
|
|
d22b6bc7d1 | ||
|
|
24852cda3d | ||
|
|
99b36291d0 | ||
|
|
1a5ad81add | ||
|
|
2b3f8aacc7 | ||
|
|
61d987c54a | ||
|
|
72d1cf1c28 | ||
|
|
9ba21f638c | ||
|
|
acf4b7b839 | ||
|
|
968d4c0cd0 | ||
|
|
d69162dbf4 | ||
|
|
cad6aab534 | ||
|
|
7ab20b293f | ||
|
|
c822d0c404 | ||
|
|
f8d8bba793 | ||
|
|
43e2af4dd3 | ||
|
|
50f0add12a | ||
|
|
ada7ba004f | ||
|
|
41213d20d1 | ||
|
|
03d0b41e64 | ||
|
|
934a6bec96 | ||
|
|
841ab71c7c | ||
|
|
ae1bc1b9e7 | ||
|
|
350d39a05d | ||
|
|
9141bf1d83 | ||
|
|
ad7dc53795 | ||
|
|
5ddf470275 | ||
|
|
de7a95a775 | ||
|
|
14026162dc | ||
|
|
16666c6e75 | ||
|
|
866dd7131e | ||
|
|
b31301abb7 | ||
|
|
769e5f0bcf | ||
|
|
ce06002ce8 | ||
|
|
9119310d01 | ||
|
|
147c7a0d97 | ||
|
|
77b7f5e0aa | ||
|
|
292d9de105 | ||
|
|
f1cf29cefa | ||
|
|
9585830e1e | ||
|
|
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 |
@@ -1,7 +1,7 @@
|
||||
freebsd_task:
|
||||
name: FreeBSD
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-2
|
||||
image_family: freebsd-13-1
|
||||
env:
|
||||
PATH: /usr/local/bin:$PATH
|
||||
prep_script:
|
||||
|
||||
76
.github/workflows/almalinux-8-build.yml
vendored
Normal file
76
.github/workflows/almalinux-8-build.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: Test rsync on AlmaLinux 8
|
||||
|
||||
# Older-LTS coverage on the Fedora/RHEL family to help with backporting
|
||||
# security fixes. AlmaLinux 8 is the RHEL 8 rebuild and is the oldest
|
||||
# active LTS in this family (RHEL 8 full support runs to 2029).
|
||||
# GitHub Actions has no native runner for this family, so the job runs
|
||||
# inside an almalinux:8 container hosted on ubuntu-latest.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
pull_request:
|
||||
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
|
||||
126
.github/workflows/build.yml
vendored
126
.github/workflows/build.yml
vendored
@@ -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
|
||||
55
.github/workflows/cygwin-build.yml
vendored
Normal file
55
.github/workflows/cygwin-build.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Test rsync on Cygwin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
pull_request:
|
||||
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,clean-fname-underflow,daemon-chroot-acl,devices,dir-sgid,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
|
||||
49
.github/workflows/freebsd-build.yml
vendored
Normal file
49
.github/workflows/freebsd-build.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: Test rsync on FreeBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
pull_request:
|
||||
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
|
||||
57
.github/workflows/macos-build.yml
vendored
Normal file
57
.github/workflows/macos-build.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: Test rsync on macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
pull_request:
|
||||
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,clean-fname-underflow,daemon-chroot-acl,devices-fake,dir-sgid,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
|
||||
51
.github/workflows/netbsd-build.yml
vendored
Normal file
51
.github/workflows/netbsd-build.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Test rsync on NetBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on NetBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in NetBSD VM
|
||||
id: test
|
||||
uses: vmactions/netbsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
PATH=/usr/sbin:$PATH pkg_add autoconf automake python312
|
||||
ln -sf /usr/pkg/bin/python3.12 /usr/pkg/bin/python3
|
||||
run: |
|
||||
uname -a
|
||||
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./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
|
||||
52
.github/workflows/openbsd-build.yml
vendored
Normal file
52
.github/workflows/openbsd-build.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Test rsync on OpenBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on OpenBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in OpenBSD VM
|
||||
id: test
|
||||
uses: vmactions/openbsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg_add -I bash autoconf%2.71 automake%1.16
|
||||
run: |
|
||||
uname -a
|
||||
export AUTOCONF_VERSION=2.71
|
||||
export AUTOMAKE_VERSION=1.16
|
||||
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./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
|
||||
49
.github/workflows/solaris-build.yml
vendored
Normal file
49
.github/workflows/solaris-build.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: Test rsync on Solaris
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
pull_request:
|
||||
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
|
||||
59
.github/workflows/ubuntu-22.04-build.yml
vendored
Normal file
59
.github/workflows/ubuntu-22.04-build.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
|
||||
# Older-LTS coverage to help with backporting security fixes. ubuntu-22.04
|
||||
# is currently the oldest GitHub Actions runner image (20.04 was retired
|
||||
# in April 2025).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
pull_request:
|
||||
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
|
||||
55
.github/workflows/ubuntu-build.yml
vendored
Normal file
55
.github/workflows/ubuntu-build.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Test rsync on Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
pull_request:
|
||||
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
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,6 +16,7 @@ aclocal.m4
|
||||
/proto.h
|
||||
/proto.h-tstamp
|
||||
/rsync*.[15]
|
||||
/rrsync
|
||||
/rrsync*.1
|
||||
/rsync*.html
|
||||
/rrsync*.html
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
47
Makefile.in
47
Makefile.in
@@ -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 \
|
||||
@@ -45,7 +47,7 @@ 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
|
||||
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
|
||||
@@ -55,12 +57,13 @@ TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/perms
|
||||
|
||||
# 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
|
||||
|
||||
@@ -146,13 +151,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)
|
||||
@@ -174,6 +179,14 @@ T_UNSAFE_OBJ = t_unsafe.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/sn
|
||||
t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
|
||||
|
||||
T_CHMOD_SECURE_OBJ = t_chmod_secure.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o
|
||||
t_chmod_secure$(EXEEXT): $(T_CHMOD_SECURE_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_CHMOD_SECURE_OBJ) $(LIBS)
|
||||
|
||||
T_SECURE_RELPATH_OBJ = t_secure_relpath.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o
|
||||
t_secure_relpath$(EXEEXT): $(T_SECURE_RELPATH_OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_SECURE_RELPATH_OBJ) $(LIBS)
|
||||
|
||||
.PHONY: conf
|
||||
conf: configure.sh config.h.in
|
||||
|
||||
@@ -271,9 +284,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:
|
||||
@@ -330,6 +343,14 @@ 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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
4
access.c
4
access.c
@@ -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;
|
||||
|
||||
7
acls.c
7
acls.c
@@ -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
|
||||
@@ -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))
|
||||
|
||||
@@ -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"))) {
|
||||
|
||||
16
backup.c
16
backup.c
@@ -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);
|
||||
|
||||
4
batch.c
4
batch.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1999 Weiss
|
||||
* Copyright (C) 2004 Chris Shoemaker
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2022 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -194,7 +194,7 @@ static int write_opt(const char *opt, const char *arg)
|
||||
{
|
||||
int len = strlen(opt);
|
||||
int err = write(batch_sh_fd, " ", 1) != 1;
|
||||
err = write(batch_sh_fd, opt, len) != len ? 1 : 0;
|
||||
err = write(batch_sh_fd, opt, len) != len ? 1 : 0;
|
||||
if (arg) {
|
||||
err |= write(batch_sh_fd, "=", 1) != 1;
|
||||
err |= write_arg(arg);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Simple byteorder handling.
|
||||
*
|
||||
* Copyright (C) 1992-1995 Andrew Tridgell
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -129,4 +129,3 @@ SIVAL(char *buf, int pos, uint32 val)
|
||||
{
|
||||
SIVALu((uchar*)buf, pos, val);
|
||||
}
|
||||
|
||||
|
||||
477
checksum.c
477
checksum.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-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
|
||||
@@ -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)
|
||||
@@ -204,7 +302,22 @@ uint32 get_checksum1(char *buf1, int32 len)
|
||||
|
||||
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 +335,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 +365,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 +386,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 +404,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 +490,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 +512,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 +528,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 +537,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 +594,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 +613,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 +640,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 +677,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 +706,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 +725,74 @@ 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 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()) {
|
||||
|
||||
@@ -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];
|
||||
|
||||
165
clientserver.c
165
clientserver.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 2001-2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2002-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])) {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
208
compat.c
208
compat.c
@@ -60,13 +60,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 +82,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 +97,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 +131,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 +180,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 +203,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 +214,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 +227,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 +263,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 +294,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 +329,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 +377,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 +410,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 +443,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 +474,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 +535,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 +566,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 +618,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 +815,73 @@ 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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int get_subprotocol_version()
|
||||
{
|
||||
#if SUBPROTOCOL_VERSION != 0
|
||||
return protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
225
configure.ac
225
configure.ac
@@ -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,19 @@ 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
|
||||
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,7 +388,7 @@ 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>
|
||||
@@ -422,11 +399,11 @@ main()
|
||||
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
|
||||
@@ -456,7 +433,8 @@ 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)],
|
||||
[AC_DEFINE(USE_OPENSSL)
|
||||
enable_openssl=yes],
|
||||
[err_msg="$err_msg$nl- Failed to find MD5_Init function in openssl crypto lib.";
|
||||
no_lib="$no_lib openssl"])
|
||||
else
|
||||
@@ -464,10 +442,67 @@ if test x"$enable_openssl" != x"no"; then
|
||||
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 +526,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 +547,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,7 +572,7 @@ 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 "how to generate (or fetch) manpages:"
|
||||
echo " https://github.com/WayneD/rsync/blob/master/INSTALL.md"
|
||||
echo ""
|
||||
echo "To disable one or more features, the relevant configure options are:"
|
||||
@@ -598,7 +633,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,
|
||||
@@ -1040,21 +1079,6 @@ 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)
|
||||
@@ -1091,7 +1115,7 @@ else
|
||||
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 +1281,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 +1390,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
6
delete.c
6
delete.c
@@ -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";
|
||||
|
||||
341
exclude.c
341
exclude.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2002 Martin Pool
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-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
|
||||
@@ -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. */
|
||||
|
||||
92
flist.c
92
flist.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2002-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
|
||||
@@ -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;
|
||||
@@ -144,7 +146,8 @@ void init_flist(void)
|
||||
rprintf(FINFO, "FILE_STRUCT_LEN=%d, EXTRA_LEN=%d\n",
|
||||
(int)FILE_STRUCT_LEN, (int)EXTRA_LEN);
|
||||
}
|
||||
flist_csum_len = csum_len_for_type(checksum_type, 1);
|
||||
/* Note that this isn't identical to file_sum_len in the case of CSUM_MD4_ARCHAIC: */
|
||||
flist_csum_len = csum_len_for_type(file_sum_nni->num, 1);
|
||||
|
||||
show_filelist_progress = INFO_GTE(FLIST, 1) && xfer_dirs && !am_server && !inc_recurse;
|
||||
}
|
||||
@@ -700,6 +703,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 +756,7 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
if (*thisname
|
||||
&& (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) {
|
||||
rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if (sanitize_paths)
|
||||
@@ -814,6 +818,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 +836,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 +861,19 @@ 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. */
|
||||
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 +957,20 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
#endif
|
||||
linkname_len = 0;
|
||||
|
||||
if (copy_devices && IS_DEVICE(mode)) {
|
||||
/* This is impossible in the official release, but some pre-release patches
|
||||
* didn't convert the device into a file before sending, so we'll do it here
|
||||
* (even though the length is typically 0 and any checksum data is zeros). */
|
||||
mode = S_IFREG | (mode & ACCESSPERMS);
|
||||
modtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
|
||||
real_ISREG_entry = 0;
|
||||
} else
|
||||
real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
|
||||
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
create_object:
|
||||
if (preserve_hard_links) {
|
||||
if (protocol_version < 28 && S_ISREG(mode))
|
||||
if (protocol_version < 28 && real_ISREG_entry)
|
||||
xflags |= XMIT_HLINKED;
|
||||
if (xflags & XMIT_HLINKED)
|
||||
extra_len += (inc_recurse+1) * EXTRA_LEN;
|
||||
@@ -973,6 +999,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if (*thisname == '/' ? thisname[1] != '.' || thisname[2] != '\0' : *thisname != '.' || thisname[1] != '\0') {
|
||||
int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
|
||||
if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
|
||||
&& filter_list.head && check_server_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
|
||||
rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) {
|
||||
rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
if (inc_recurse && S_ISDIR(mode)) {
|
||||
if (one_file_system) {
|
||||
/* Room to save the dir's device for -x */
|
||||
@@ -1160,8 +1199,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
}
|
||||
#endif
|
||||
|
||||
if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
|
||||
if (S_ISREG(mode))
|
||||
if (always_checksum && (real_ISREG_entry || protocol_version < 28)) {
|
||||
if (real_ISREG_entry)
|
||||
bp = F_SUM(file);
|
||||
else {
|
||||
/* Prior to 28, we get a useless set of nulls. */
|
||||
@@ -1360,6 +1399,18 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
|
||||
linkname_len = 0;
|
||||
#endif
|
||||
|
||||
if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
|
||||
if (st.st_size == 0) {
|
||||
int fd = do_open_checklinks(fname);
|
||||
if (fd >= 0) {
|
||||
st.st_size = get_device_size(fd, fname);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
st.st_mode = S_IFREG | (st.st_mode & ACCESSPERMS);
|
||||
st.st_mtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
|
||||
}
|
||||
|
||||
#ifdef ST_MTIME_NSEC
|
||||
if (st.ST_MTIME_NSEC && protocol_version >= 31)
|
||||
extra_len += EXTRA_LEN;
|
||||
@@ -2544,6 +2595,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 +2666,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
|
||||
rprintf(FERROR,
|
||||
"ABORTING due to invalid path from sender: %s/%s\n",
|
||||
cur_dir, file->basename);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
exit_cleanup(RERR_UNSUPPORTED);
|
||||
}
|
||||
good_dirname = cur_dir;
|
||||
}
|
||||
|
||||
85
generator.c
85
generator.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2000 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-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
|
||||
@@ -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;
|
||||
@@ -229,11 +229,13 @@ static int read_delay_line(char *buf, int *flags_p)
|
||||
*flags_p = 0;
|
||||
|
||||
if (sscanf(bp, "%x ", &mode) != 1) {
|
||||
invalid_data:
|
||||
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
|
||||
return -1;
|
||||
goto invalid_data;
|
||||
}
|
||||
past_space = strchr(bp, ' ') + 1;
|
||||
past_space = strchr(bp, ' ');
|
||||
if (!past_space) {
|
||||
goto invalid_data;
|
||||
}
|
||||
past_space++;
|
||||
len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
|
||||
read_pos = j + 1;
|
||||
|
||||
@@ -247,6 +249,10 @@ static int read_delay_line(char *buf, int *flags_p)
|
||||
memcpy(buf, past_space, len);
|
||||
|
||||
return mode;
|
||||
|
||||
invalid_data:
|
||||
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void do_delayed_deletions(char *delbuf)
|
||||
@@ -532,7 +538,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
|
||||
@@ -875,9 +881,12 @@ static struct file_struct *find_fuzzy(struct file_struct *file, struct file_list
|
||||
len = strlen(name);
|
||||
suf = find_filename_suffix(name, len, &suf_len);
|
||||
|
||||
dist = fuzzy_distance(name, len, fname, fname_len);
|
||||
/* Add some extra weight to how well the suffixes match. */
|
||||
dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len) * 10;
|
||||
dist = fuzzy_distance(name, len, fname, fname_len, lowest_dist);
|
||||
/* Add some extra weight to how well the suffixes match unless we've already disqualified
|
||||
* this file based on a heuristic. */
|
||||
if (dist < 0xFFFF0000U) {
|
||||
dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len, 0xFFFF0000U) * 10;
|
||||
}
|
||||
if (DEBUG_GTE(FUZZY, 2)) {
|
||||
rprintf(FINFO, "fuzzy distance for %s = %d.%05d\n",
|
||||
f_name(fp, NULL), (int)(dist>>16), (int)(dist&0xFFFF));
|
||||
@@ -981,7 +990,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
if (find_exact_for_existing) {
|
||||
if (alt_dest_type == LINK_DEST && real_st.st_dev == sxp->st.st_dev && real_st.st_ino == sxp->st.st_ino)
|
||||
return -1;
|
||||
if (do_unlink(fname) < 0 && errno != ENOENT)
|
||||
if (do_unlink_at(fname) < 0 && errno != ENOENT)
|
||||
goto got_nothing_for_ya;
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
@@ -1109,7 +1118,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
&& !IS_SPECIAL(file->mode) && !IS_DEVICE(file->mode)
|
||||
#endif
|
||||
&& !S_ISDIR(file->mode)) {
|
||||
if (do_link(cmpbuf, fname) < 0) {
|
||||
if (do_link_at(cmpbuf, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to hard-link %s with %s",
|
||||
cmpbuf, fname);
|
||||
@@ -1312,7 +1321,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 +1433,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 +1475,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
itemize(fnamecmp, file, ndx, statret, &sx,
|
||||
statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
|
||||
}
|
||||
if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
|
||||
if (real_ret != 0 && do_mkdir_at(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
|
||||
if (!relative_paths || errno != ENOENT
|
||||
|| make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0
|
||||
|| (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
|| (do_mkdir_at(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"recv_generator: mkdir %s failed",
|
||||
full_fname(fname));
|
||||
@@ -1496,7 +1505,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
#ifdef HAVE_CHMOD
|
||||
if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
|
||||
mode_t mode = file->mode | S_IRWXU;
|
||||
if (do_chmod(fname, mode) < 0) {
|
||||
if (do_chmod_at(fname, mode) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to modify permissions on %s",
|
||||
full_fname(fname));
|
||||
@@ -1793,13 +1802,19 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (write_devices && IS_DEVICE(sx.st.st_mode) && sx.st.st_size == 0) {
|
||||
/* This early open into fd skips the regular open below. */
|
||||
if ((fd = do_open_nofollow(fnamecmp, O_RDONLY)) >= 0)
|
||||
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
|
||||
}
|
||||
|
||||
if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
|
||||
;
|
||||
else if (fnamecmp_type >= FNAMECMP_FUZZY)
|
||||
;
|
||||
else if (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 +1828,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto cleanup;
|
||||
return_with_success:
|
||||
if (!dry_run)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
send_msg_success(fname, ndx);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -1858,7 +1873,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
if ((fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
|
||||
if (fd < 0 && (fd = do_open_checklinks(fnamecmp)) < 0) {
|
||||
rsyserr(FERROR, errno, "failed to open %s, continuing",
|
||||
full_fname(fnamecmp));
|
||||
pretend_missing:
|
||||
@@ -1875,11 +1890,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
|
||||
if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
|
||||
if (!(backupptr = get_backup_name(fname))) {
|
||||
close(fd);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
|
||||
close(fd);
|
||||
goto pretend_missing;
|
||||
}
|
||||
if (robust_unlink(backupptr) && errno != ENOENT) {
|
||||
@@ -1887,14 +1900,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
full_fname(backupptr));
|
||||
unmake_file(back_file);
|
||||
back_file = NULL;
|
||||
close(fd);
|
||||
goto cleanup;
|
||||
}
|
||||
if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
if ((f_copy = do_open_at(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "open %s", full_fname(backupptr));
|
||||
unmake_file(back_file);
|
||||
back_file = NULL;
|
||||
close(fd);
|
||||
goto cleanup;
|
||||
}
|
||||
fnamecmp_type = FNAMECMP_BACKUP;
|
||||
@@ -1945,7 +1956,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
write_sum_head(f_out, NULL);
|
||||
else if (sx.st.st_size <= 0) {
|
||||
write_sum_head(f_out, NULL);
|
||||
close(fd);
|
||||
} else {
|
||||
if (generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy) < 0) {
|
||||
rprintf(FWARNING,
|
||||
@@ -1953,10 +1963,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
fnamecmp);
|
||||
write_sum_head(f_out, NULL);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (back_file) {
|
||||
int save_preserve_xattrs = preserve_xattrs;
|
||||
if (f_copy >= 0)
|
||||
@@ -2011,7 +2022,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
|
||||
if (slnk) {
|
||||
#ifdef SUPPORT_LINKS
|
||||
if (do_symlink(slnk, create_name) < 0) {
|
||||
if (do_symlink_at(slnk, create_name) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
|
||||
full_fname(create_name), slnk);
|
||||
return 0;
|
||||
@@ -2027,7 +2038,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
return 0;
|
||||
#endif
|
||||
} else {
|
||||
if (do_mknod(create_name, file->mode, rdev) < 0) {
|
||||
if (do_mknod_at(create_name, file->mode, rdev) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "mknod %s failed",
|
||||
full_fname(create_name));
|
||||
return 0;
|
||||
@@ -2035,10 +2046,14 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
}
|
||||
|
||||
if (!skip_atomic) {
|
||||
if (do_rename(tmpname, fname) < 0) {
|
||||
if (do_rename_at(tmpname, fname) < 0) {
|
||||
char *full_tmpname = strdup(full_fname(tmpname));
|
||||
if (full_tmpname == NULL)
|
||||
out_of_memory("atomic_create");
|
||||
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
|
||||
full_fname(tmpname), full_fname(fname));
|
||||
do_unlink(tmpname);
|
||||
full_tmpname, full_fname(fname));
|
||||
free(full_tmpname);
|
||||
do_unlink_at(tmpname);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2102,7 +2117,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
|
||||
continue;
|
||||
fname = f_name(file, NULL);
|
||||
if (fix_dir_perms)
|
||||
do_chmod(fname, file->mode);
|
||||
do_chmod_at(fname, file->mode);
|
||||
if (need_retouch_dir_times) {
|
||||
STRUCT_STAT st;
|
||||
if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
|
||||
@@ -2137,6 +2152,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
|
||||
if (send_failed)
|
||||
ndx = get_hlink_num();
|
||||
flist = flist_for_ndx(ndx, "check_for_finished_files.1");
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
file = flist->files[ndx - flist->ndx_start];
|
||||
assert(file->flags & FLAG_HLINKED);
|
||||
if (send_failed)
|
||||
@@ -2165,6 +2182,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
|
||||
|
||||
flist = cur_flist;
|
||||
cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2");
|
||||
if (ndx < cur_flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
|
||||
file = cur_flist->files[ndx - cur_flist->ndx_start];
|
||||
if (solo_file)
|
||||
|
||||
174
hashtable.c
174
hashtable.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Routines to provide a memory-efficient hashtable.
|
||||
*
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -350,6 +350,9 @@ void *hashtable_find(struct hashtable *tbl, int64 key, void *data_when_new)
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define NON_ZERO_32(x) ((x) ? (x) : (uint32_t)1)
|
||||
#define NON_ZERO_64(x, y) ((x) || (y) ? (y) | (int64)(x) << 32 | (y) : (int64)1)
|
||||
|
||||
uint32_t hashlittle(const void *key, size_t length)
|
||||
{
|
||||
uint32_t a,b,c; /* internal state */
|
||||
@@ -390,7 +393,7 @@ uint32_t hashlittle(const void *key, size_t length)
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||
case 1 : a+=k8[0]; break;
|
||||
case 0 : return c;
|
||||
case 0 : return NON_ZERO_32(c);
|
||||
}
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
@@ -436,7 +439,7 @@ uint32_t hashlittle(const void *key, size_t length)
|
||||
break;
|
||||
case 1 : a+=k8[0];
|
||||
break;
|
||||
case 0 : return c; /* zero length requires no mixing */
|
||||
case 0 : return NON_ZERO_32(c); /* zero length requires no mixing */
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
@@ -489,10 +492,171 @@ uint32_t hashlittle(const void *key, size_t length)
|
||||
/* FALLTHROUGH */
|
||||
case 1 : a+=k[0];
|
||||
break;
|
||||
case 0 : return c;
|
||||
case 0 : return NON_ZERO_32(c);
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return c;
|
||||
return NON_ZERO_32(c);
|
||||
}
|
||||
|
||||
#if SIZEOF_INT64 >= 8
|
||||
/*
|
||||
* hashlittle2: return 2 32-bit hash values joined into an int64.
|
||||
*
|
||||
* This is identical to hashlittle(), except it returns two 32-bit hash
|
||||
* values instead of just one. This is good enough for hash table
|
||||
* lookup with 2^^64 buckets, or if you want a second hash if you're not
|
||||
* happy with the first, or if you want a probably-unique 64-bit ID for
|
||||
* the key. *pc is better mixed than *pb, so use *pc first. If you want
|
||||
* a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
|
||||
*/
|
||||
int64 hashlittle2(const void *key, size_t length)
|
||||
{
|
||||
uint32_t a,b,c; /* internal state */
|
||||
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = 0xdeadbeef + ((uint32_t)length);
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||
case 1 : a+=k8[0]; break;
|
||||
case 0 : return NON_ZERO_64(b, c);
|
||||
}
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*--------------- all but last block: aligned reads and different mixing */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=k[4];
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=k[2];
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=k[0];
|
||||
break;
|
||||
case 1 : a+=k8[0];
|
||||
break;
|
||||
case 0 : return NON_ZERO_64(b, c); /* zero length strings require no mixing */
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*-------------------------------- last block: affect all 32 bits of (c) */
|
||||
switch(length) /* all the case statements fall through */
|
||||
{
|
||||
case 12: c+=((uint32_t)k[11])<<24;
|
||||
/* FALLTHROUGH */
|
||||
case 11: c+=((uint32_t)k[10])<<16;
|
||||
/* FALLTHROUGH */
|
||||
case 10: c+=((uint32_t)k[9])<<8;
|
||||
/* FALLTHROUGH */
|
||||
case 9 : c+=k[8];
|
||||
/* FALLTHROUGH */
|
||||
case 8 : b+=((uint32_t)k[7])<<24;
|
||||
/* FALLTHROUGH */
|
||||
case 7 : b+=((uint32_t)k[6])<<16;
|
||||
/* FALLTHROUGH */
|
||||
case 6 : b+=((uint32_t)k[5])<<8;
|
||||
/* FALLTHROUGH */
|
||||
case 5 : b+=k[4];
|
||||
/* FALLTHROUGH */
|
||||
case 4 : a+=((uint32_t)k[3])<<24;
|
||||
/* FALLTHROUGH */
|
||||
case 3 : a+=((uint32_t)k[2])<<16;
|
||||
/* FALLTHROUGH */
|
||||
case 2 : a+=((uint32_t)k[1])<<8;
|
||||
/* FALLTHROUGH */
|
||||
case 1 : a+=k[0];
|
||||
break;
|
||||
case 0 : return NON_ZERO_64(b, c);
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return NON_ZERO_64(b, c);
|
||||
}
|
||||
#else
|
||||
#define hashlittle2(key, len) hashlittle(key, len)
|
||||
#endif
|
||||
|
||||
10
hlink.c
10
hlink.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2022 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -117,7 +117,7 @@ static void match_gnums(int32 *ndx_list, int ndx_count)
|
||||
struct ht_int32_node *node = NULL;
|
||||
int32 gnum, gnum_next;
|
||||
|
||||
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)()) hlink_compare_gnum);
|
||||
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)(const void*, const void*))hlink_compare_gnum);
|
||||
|
||||
for (from = 0; from < ndx_count; from++) {
|
||||
file = hlink_flist->sorted[ndx_list[from]];
|
||||
@@ -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) {
|
||||
|
||||
2
ifuncs.h
2
ifuncs.h
@@ -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
|
||||
|
||||
113
io.c
113
io.c
@@ -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;
|
||||
@@ -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'
|
||||
@@ -1567,14 +1603,15 @@ static void read_a_msg(void)
|
||||
}
|
||||
break;
|
||||
case MSG_SUCCESS:
|
||||
if (msg_bytes != 4) {
|
||||
if (msg_bytes != (local_server ? 4+8+8 : 4)) {
|
||||
invalid_msg:
|
||||
rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
|
||||
tag, (unsigned long)msg_bytes, who_am_i(),
|
||||
inc_recurse ? "/inc" : "");
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
val = raw_read_int();
|
||||
raw_read_buf(num_dev_ino_buf, msg_bytes);
|
||||
val = IVAL(num_dev_ino_buf, 0);
|
||||
iobuf.in_multiplexed = 1;
|
||||
if (am_generator)
|
||||
got_flist_entry_status(FES_SUCCESS, val);
|
||||
@@ -1751,6 +1788,13 @@ int32 read_int(int f)
|
||||
return num;
|
||||
}
|
||||
|
||||
uint32 read_uint(int f)
|
||||
{
|
||||
char b[4];
|
||||
read_buf(f, b, 4);
|
||||
return IVAL(b, 0);
|
||||
}
|
||||
|
||||
int32 read_varint(int f)
|
||||
{
|
||||
union {
|
||||
@@ -1824,6 +1868,45 @@ int64 read_varlong(int f, uchar min_bytes)
|
||||
return u.x;
|
||||
}
|
||||
|
||||
/* Read an int32 and verify lo <= v <= hi. On out-of-range, abort with a
|
||||
* protocol error naming "what". The bound is co-located with the read so it
|
||||
* cannot be forgotten by a downstream user. */
|
||||
int32 read_int_bounded(int f, int32 lo, int32 hi, const char *what)
|
||||
{
|
||||
int32 v = read_int(f);
|
||||
if (v < lo || v > hi) {
|
||||
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
|
||||
what, (long)v, (long)lo, (long)hi, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* As read_int_bounded but for varint-encoded values. */
|
||||
int32 read_varint_bounded(int f, int32 lo, int32 hi, const char *what)
|
||||
{
|
||||
int32 v = read_varint(f);
|
||||
if (v < lo || v > hi) {
|
||||
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
|
||||
what, (long)v, (long)lo, (long)hi, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read a varint that will be used as a size_t. Rejects negative values
|
||||
* (which would wrap to ~SIZE_MAX) and values exceeding the supplied max. */
|
||||
size_t read_varint_size(int f, size_t max, const char *what)
|
||||
{
|
||||
int32 v = read_varint(f);
|
||||
if (v < 0 || (size_t)v > max) {
|
||||
rprintf(FERROR, "wire size %s out of range: %ld > %lu [%s]\n",
|
||||
what, (long)v, (unsigned long)max, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return (size_t)v;
|
||||
}
|
||||
|
||||
int64 read_longint(int f)
|
||||
{
|
||||
#if SIZEOF_INT64 >= 8
|
||||
@@ -1843,6 +1926,7 @@ int64 read_longint(int f)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Debugging note: this will be named read_buf_() when using an external zlib. */
|
||||
void read_buf(int f, char *buf, size_t len)
|
||||
{
|
||||
if (f != iobuf.in_fd) {
|
||||
@@ -1929,6 +2013,21 @@ void read_sum_head(int f, struct sum_struct *sum)
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
/* Guard against integer overflow in downstream allocations sized by
|
||||
* count*element_size. my_alloc uses divide-not-multiply so it is
|
||||
* already wraparound-safe, but checking here gives a clearer error
|
||||
* and also covers the (size_t)count * xfer_sum_len arithmetic that
|
||||
* is performed *before* reaching my_alloc. */
|
||||
if (xfer_sum_len > 0 && (size_t)sum->count > SIZE_MAX / (size_t)xfer_sum_len) {
|
||||
rprintf(FERROR, "Invalid checksum count %ld (too large) [%s]\n",
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if ((size_t)sum->count > SIZE_MAX / sizeof(struct sum_buf)) {
|
||||
rprintf(FERROR, "Invalid checksum count %ld (sum_buf overflow) [%s]\n",
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
sum->blength = read_int(f);
|
||||
if (sum->blength < 0 || sum->blength > max_blength) {
|
||||
rprintf(FERROR, "Invalid block length %ld [%s]\n",
|
||||
@@ -1936,7 +2035,7 @@ void read_sum_head(int f, struct sum_struct *sum)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
|
||||
if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
|
||||
if (sum->s2length < 0 || sum->s2length > xfer_sum_len) {
|
||||
rprintf(FERROR, "Invalid checksum length %d [%s]\n",
|
||||
sum->s2length, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
|
||||
2
itypes.h
2
itypes.h
@@ -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,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 */
|
||||
|
||||
19
lib/md5.c
19
lib/md5.c
@@ -2,7 +2,7 @@
|
||||
* RFC 1321 compliant MD5 implementation
|
||||
*
|
||||
* Copyright (C) 2001-2003 Christophe Devine
|
||||
* Copyright (C) 2007-2020 Wayne Davison
|
||||
* Copyright (C) 2007-2022 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
#ifndef USE_OPENSSL
|
||||
void md5_begin(md_context *ctx)
|
||||
{
|
||||
ctx->A = 0x67452301;
|
||||
@@ -148,7 +147,10 @@ static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
|
||||
ctx->D += D;
|
||||
}
|
||||
|
||||
#if defined HAVE_ASM && CSUM_CHUNK == 64
|
||||
#ifdef USE_MD5_ASM
|
||||
#if CSUM_CHUNK != 64
|
||||
#error The MD5 ASM code does not support CSUM_CHUNK != 64
|
||||
#endif
|
||||
extern void md5_process_asm(md_context *ctx, const void *data, size_t num);
|
||||
#endif
|
||||
|
||||
@@ -176,20 +178,20 @@ 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);
|
||||
@@ -221,9 +223,8 @@ void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
|
||||
SIVALu(digest, 8, ctx->C);
|
||||
SIVALu(digest, 12, ctx->D);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_MD5
|
||||
#ifdef TEST_MD5 /* { */
|
||||
|
||||
void get_md5(uchar *out, const uchar *input, int n)
|
||||
{
|
||||
@@ -317,4 +318,4 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* } */
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* The include file for both the MD4 and MD5 routines. */
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include "openssl/md4.h"
|
||||
#include "openssl/md5.h"
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#endif
|
||||
#include "md-defines.h"
|
||||
|
||||
@@ -17,13 +17,6 @@ void mdfour_begin(md_context *md);
|
||||
void mdfour_update(md_context *md, const uchar *in, uint32 length);
|
||||
void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN]);
|
||||
|
||||
#ifndef USE_OPENSSL
|
||||
#define MD5_CTX md_context
|
||||
#define MD5_Init md5_begin
|
||||
#define MD5_Update md5_update
|
||||
#define MD5_Final(digest, cptr) md5_result(cptr, digest)
|
||||
|
||||
void md5_begin(md_context *ctx);
|
||||
void md5_update(md_context *ctx, const uchar *input, uint32 length);
|
||||
void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN]);
|
||||
#endif
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
178
lib/sysacls.c
178
lib/sysacls.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
24
log.c
24
log.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 2000-2001 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-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],
|
||||
|
||||
70
main.c
70
main.c
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2003-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;
|
||||
@@ -103,7 +104,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 +237,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()
|
||||
@@ -391,9 +392,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 +487,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 +617,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 +669,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 +706,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, '/');
|
||||
@@ -1079,6 +1094,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);
|
||||
@@ -1434,6 +1450,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 +1492,10 @@ 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;
|
||||
|
||||
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)) {
|
||||
@@ -1738,6 +1767,17 @@ int main(int argc,char *argv[])
|
||||
|
||||
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));
|
||||
|
||||
/* Even a non-daemon runs needs the default config values to be set, e.g.
|
||||
|
||||
31
match.c
31
match.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2003-2020 Wayne Davison
|
||||
* Copyright (C) 2003-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,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];
|
||||
@@ -145,6 +147,9 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
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",
|
||||
|
||||
200
md-convert
200
md-convert
@@ -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,27 @@ 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]+')
|
||||
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 +143,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 +190,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 +214,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 +226,26 @@ 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')
|
||||
|
||||
|
||||
def html_via_commonmark(txt):
|
||||
@@ -226,6 +256,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 +266,18 @@ 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_txt_start = None,
|
||||
target_suf = '',
|
||||
)
|
||||
|
||||
if st.want_manpage:
|
||||
@@ -260,6 +300,20 @@ 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_starttag(self, tag, attrs_list):
|
||||
st = self.state
|
||||
@@ -300,7 +354,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 +382,25 @@ 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:')):
|
||||
pass # nothing to check
|
||||
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
|
||||
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
|
||||
|
||||
@@ -343,13 +416,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 +460,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 +472,22 @@ class TransformHtml(HTMLParser):
|
||||
st.at_first_tag_in_dd = False
|
||||
elif tag == 'hr':
|
||||
return
|
||||
elif tag == 'a':
|
||||
if 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 +506,56 @@ 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 = 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 +569,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 +599,8 @@ def htmlify(txt):
|
||||
|
||||
def warn(*msg):
|
||||
print(*msg, file=sys.stderr)
|
||||
global warning_count
|
||||
warning_count += 1
|
||||
|
||||
|
||||
def die(*msg):
|
||||
@@ -454,11 +609,12 @@ 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('--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 +630,5 @@ if __name__ == '__main__':
|
||||
gfm_parser = None
|
||||
|
||||
main()
|
||||
if warning_count:
|
||||
sys.exit(1)
|
||||
|
||||
12
mkgitver
12
mkgitver
@@ -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
|
||||
|
||||
217
options.c
217
options.c
@@ -27,6 +27,8 @@
|
||||
extern int module_id;
|
||||
extern int local_server;
|
||||
extern int sanitize_paths;
|
||||
extern int trust_sender_args;
|
||||
extern int trust_sender_filter;
|
||||
extern unsigned int module_dirlen;
|
||||
extern filter_rule_list filter_list;
|
||||
extern filter_rule_list daemon_filter_list;
|
||||
@@ -47,6 +49,7 @@ int append_mode = 0;
|
||||
int keep_dirlinks = 0;
|
||||
int copy_dirlinks = 0;
|
||||
int copy_links = 0;
|
||||
int copy_devices = 0;
|
||||
int write_devices = 0;
|
||||
int preserve_links = 0;
|
||||
int preserve_hard_links = 0;
|
||||
@@ -63,6 +66,7 @@ int preserve_atimes = 0;
|
||||
int preserve_crtimes = 0;
|
||||
int omit_dir_times = 0;
|
||||
int omit_link_times = 0;
|
||||
int trust_sender = 0;
|
||||
int update_only = 0;
|
||||
int open_noatime = 0;
|
||||
int cvs_exclude = 0;
|
||||
@@ -102,17 +106,27 @@ int filesfrom_fd = -1;
|
||||
char *filesfrom_host = NULL;
|
||||
int eol_nulls = 0;
|
||||
int protect_args = -1;
|
||||
int old_style_args = -1;
|
||||
int human_readable = 1;
|
||||
int recurse = 0;
|
||||
int mkpath_dest_arg = 0;
|
||||
int allow_inc_recurse = 1;
|
||||
int xfer_dirs = -1;
|
||||
int am_daemon = 0;
|
||||
/* Set after a successful per-module chroot ("use chroot = yes") in
|
||||
* clientserver.c. NOT set for the daemon-level "daemon chroot = /X"
|
||||
* chroot: that confines path resolution to /X, but module paths
|
||||
* /X/modA, /X/modB, etc. are not chroot boundaries, so the per-module
|
||||
* symlink-race defenses (secure_relative_open() / do_*_at() in
|
||||
* syscall.c, gated by `am_daemon && !am_chrooted`) must still fire
|
||||
* even when the daemon is inside a daemon chroot. */
|
||||
int am_chrooted = 0;
|
||||
int connect_timeout = 0;
|
||||
int keep_partial = 0;
|
||||
int safe_symlinks = 0;
|
||||
int copy_unsafe_links = 0;
|
||||
int munge_symlinks = 0;
|
||||
int use_secure_symlinks = 0;
|
||||
int size_only = 0;
|
||||
int daemon_bwlimit = 0;
|
||||
int bwlimit = 0;
|
||||
@@ -291,7 +305,7 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = {
|
||||
DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
|
||||
DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"),
|
||||
DEBUG_WORD(EXIT, W_CLI|W_SRV, "Debug exit events (levels 1-3)"),
|
||||
DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-2)"),
|
||||
DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-3)"),
|
||||
DEBUG_WORD(FLIST, W_SND|W_REC, "Debug file-list operations (levels 1-4)"),
|
||||
DEBUG_WORD(FUZZY, W_REC, "Debug fuzzy scoring (levels 1-2)"),
|
||||
DEBUG_WORD(GENR, W_REC, "Debug generator functions"),
|
||||
@@ -577,7 +591,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
|
||||
OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
|
||||
OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
|
||||
OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
|
||||
OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS,
|
||||
OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS,
|
||||
OPT_STOP_AFTER, OPT_STOP_AT,
|
||||
OPT_REFUSED_BASE = 9000};
|
||||
|
||||
@@ -655,6 +669,7 @@ static struct poptOption long_options[] = {
|
||||
{"no-D", 0, POPT_ARG_NONE, 0, OPT_NO_D, 0, 0 },
|
||||
{"devices", 0, POPT_ARG_VAL, &preserve_devices, 1, 0, 0 },
|
||||
{"no-devices", 0, POPT_ARG_VAL, &preserve_devices, 0, 0, 0 },
|
||||
{"copy-devices", 0, POPT_ARG_NONE, ©_devices, 0, 0, 0 },
|
||||
{"write-devices", 0, POPT_ARG_VAL, &write_devices, 1, 0, 0 },
|
||||
{"no-write-devices", 0, POPT_ARG_VAL, &write_devices, 0, 0, 0 },
|
||||
{"specials", 0, POPT_ARG_VAL, &preserve_specials, 1, 0, 0 },
|
||||
@@ -780,9 +795,14 @@ static struct poptOption long_options[] = {
|
||||
{"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 },
|
||||
{"from0", '0', POPT_ARG_VAL, &eol_nulls, 1, 0, 0},
|
||||
{"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 0, 0, 0},
|
||||
{"protect-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
|
||||
{"old-args", 0, POPT_ARG_NONE, 0, OPT_OLD_ARGS, 0, 0},
|
||||
{"no-old-args", 0, POPT_ARG_VAL, &old_style_args, 0, 0, 0},
|
||||
{"secluded-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
|
||||
{"no-secluded-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
|
||||
{"protect-args", 0, POPT_ARG_VAL, &protect_args, 1, 0, 0},
|
||||
{"no-protect-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
|
||||
{"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
|
||||
{"trust-sender", 0, POPT_ARG_VAL, &trust_sender, 1, 0, 0},
|
||||
{"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 },
|
||||
{"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 },
|
||||
{"usermap", 0, POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 },
|
||||
@@ -941,11 +961,12 @@ static void set_refuse_options(void)
|
||||
if (!am_daemon
|
||||
|| op->shortName == 'e' /* Required for compatibility flags */
|
||||
|| op->shortName == '0' /* --from0 just modifies --files-from, so refuse that instead (or not) */
|
||||
|| op->shortName == 's' /* --protect-args is always OK */
|
||||
|| op->shortName == 's' /* --secluded-args is always OK */
|
||||
|| op->shortName == 'n' /* --dry-run is always OK */
|
||||
|| strcmp("iconv", longName) == 0
|
||||
|| strcmp("no-iconv", longName) == 0
|
||||
|| strcmp("checksum-seed", longName) == 0
|
||||
|| strcmp("copy-devices", longName) == 0 /* disable wild-match (it gets refused below) */
|
||||
|| strcmp("write-devices", longName) == 0 /* disable wild-match (it gets refused below) */
|
||||
|| strcmp("log-format", longName) == 0 /* aka out-format (NOT log-file-format) */
|
||||
|| strcmp("sender", longName) == 0
|
||||
@@ -957,6 +978,7 @@ static void set_refuse_options(void)
|
||||
assert(list_end != NULL);
|
||||
|
||||
if (am_daemon) { /* Refused by default, but can be accepted via a negated exact match. */
|
||||
parse_one_refuse_match(0, "copy-devices", list_end);
|
||||
parse_one_refuse_match(0, "write-devices", list_end);
|
||||
}
|
||||
|
||||
@@ -1142,7 +1164,7 @@ static time_t parse_time(const char *arg)
|
||||
{
|
||||
const char *cp;
|
||||
time_t val, now = time(NULL);
|
||||
struct tm t, *today = localtime(&now);
|
||||
struct tm t, tmp, *today = localtime_r(&now, &tmp);
|
||||
int in_date, old_mday, n;
|
||||
|
||||
memset(&t, 0, sizeof t);
|
||||
@@ -1359,6 +1381,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (pc)
|
||||
poptFreeContext(pc);
|
||||
pc = poptGetContext(RSYNC_NAME, argc, argv, long_options, 0);
|
||||
if (pc == NULL) {
|
||||
strlcpy(err_buf, "poptGetContext returned NULL\n", sizeof err_buf);
|
||||
return 0;
|
||||
}
|
||||
if (!am_server) {
|
||||
poptReadDefaultConfig(pc, 0);
|
||||
popt_unalias(pc, "--daemon");
|
||||
@@ -1605,6 +1631,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
compress_choice = NULL;
|
||||
break;
|
||||
|
||||
case OPT_OLD_ARGS:
|
||||
if (old_style_args <= 0)
|
||||
old_style_args = 1;
|
||||
else
|
||||
old_style_args++;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
arg = poptGetOptArg(pc);
|
||||
if (*arg != '-') {
|
||||
@@ -1906,7 +1939,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
saw_stderr_opt = 1;
|
||||
|
||||
if (version_opt_cnt) {
|
||||
print_rsync_version(FINFO);
|
||||
print_rsync_version(version_opt_cnt > 1 && !am_server ? FNONE : FINFO);
|
||||
exit_cleanup(0);
|
||||
}
|
||||
|
||||
@@ -1922,13 +1955,28 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
max_alloc = size;
|
||||
}
|
||||
|
||||
if (old_style_args < 0) {
|
||||
if (!am_server && protect_args <= 0 && (arg = getenv("RSYNC_OLD_ARGS")) != NULL && *arg) {
|
||||
protect_args = 0;
|
||||
old_style_args = atoi(arg);
|
||||
} else
|
||||
old_style_args = 0;
|
||||
} else if (old_style_args) {
|
||||
if (protect_args > 0) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--secluded-args conflicts with --old-args.\n");
|
||||
return 0;
|
||||
}
|
||||
protect_args = 0;
|
||||
}
|
||||
|
||||
if (protect_args < 0) {
|
||||
if (am_server)
|
||||
protect_args = 0;
|
||||
else if ((arg = getenv("RSYNC_PROTECT_ARGS")) != NULL && *arg)
|
||||
protect_args = atoi(arg) ? 1 : 0;
|
||||
else {
|
||||
#ifdef RSYNC_USE_PROTECTED_ARGS
|
||||
#ifdef RSYNC_USE_SECLUDED_ARGS
|
||||
protect_args = 1;
|
||||
#else
|
||||
protect_args = 0;
|
||||
@@ -2436,6 +2484,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
}
|
||||
}
|
||||
|
||||
if (trust_sender || am_server || read_batch)
|
||||
trust_sender_args = trust_sender_filter = 1;
|
||||
else if (old_style_args || filesfrom_host != NULL)
|
||||
trust_sender_args = 1;
|
||||
|
||||
am_starting_up = 0;
|
||||
|
||||
return 1;
|
||||
@@ -2447,6 +2500,73 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
}
|
||||
|
||||
|
||||
static char SPLIT_ARG_WHEN_OLD[1];
|
||||
|
||||
/**
|
||||
* Do backslash quoting of any weird chars in "arg", append the resulting
|
||||
* string to the end of the "opt" (which gets a "=" appended if it is not
|
||||
* an empty or NULL string), and return the (perhaps malloced) result.
|
||||
* If opt is NULL, arg is considered a filename arg that allows wildcards.
|
||||
* If it is "" or any other value, it is considered an option.
|
||||
**/
|
||||
char *safe_arg(const char *opt, const char *arg)
|
||||
{
|
||||
#define SHELL_CHARS "!#$&;|<>(){}\"' \t\\"
|
||||
#define WILD_CHARS "*?[]" /* We don't allow remote brace expansion */
|
||||
BOOL is_filename_arg = !opt;
|
||||
char *escapes = is_filename_arg ? SHELL_CHARS : WILD_CHARS SHELL_CHARS;
|
||||
BOOL escape_leading_dash = is_filename_arg && *arg == '-';
|
||||
BOOL escape_leading_tilde = 0;
|
||||
int len1 = opt && *opt ? strlen(opt) + 1 : 0;
|
||||
int len2 = strlen(arg);
|
||||
int extras = escape_leading_dash ? 2 : 0;
|
||||
char *ret;
|
||||
if (!protect_args && old_style_args < 2 && (!old_style_args || (!is_filename_arg && opt != SPLIT_ARG_WHEN_OLD))) {
|
||||
const char *f;
|
||||
if (!trust_sender_args && *arg == '~'
|
||||
&& ((relative_paths && !strstr(arg, "/./"))
|
||||
|| !strchr(arg, '/'))) {
|
||||
extras++;
|
||||
escape_leading_tilde = 1;
|
||||
}
|
||||
for (f = arg; *f; f++) {
|
||||
if (strchr(escapes, *f))
|
||||
extras++;
|
||||
}
|
||||
}
|
||||
if (!len1 && !extras)
|
||||
return (char*)arg;
|
||||
ret = new_array(char, len1 + len2 + extras + 1);
|
||||
if (len1) {
|
||||
memcpy(ret, opt, len1-1);
|
||||
ret[len1-1] = '=';
|
||||
}
|
||||
if (escape_leading_dash) {
|
||||
ret[len1++] = '.';
|
||||
ret[len1++] = '/';
|
||||
extras -= 2;
|
||||
}
|
||||
if (!extras)
|
||||
memcpy(ret + len1, arg, len2);
|
||||
else {
|
||||
const char *f = arg;
|
||||
char *t = ret + len1;
|
||||
if (escape_leading_tilde)
|
||||
*t++ = '\\';
|
||||
while (*f) {
|
||||
if (*f == '\\') {
|
||||
if (!is_filename_arg || !strchr(WILD_CHARS, f[1]))
|
||||
*t++ = '\\';
|
||||
} else if (strchr(escapes, *f))
|
||||
*t++ = '\\';
|
||||
*t++ = *f++;
|
||||
}
|
||||
}
|
||||
ret[len1+len2+extras] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a filtered list of options to pass through from the
|
||||
* client to the server.
|
||||
@@ -2590,9 +2710,7 @@ void server_options(char **args, int *argc_p)
|
||||
set++;
|
||||
else
|
||||
set = iconv_opt;
|
||||
if (asprintf(&arg, "--iconv=%s", set) < 0)
|
||||
goto oom;
|
||||
args[ac++] = arg;
|
||||
args[ac++] = safe_arg("--iconv", set);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2658,33 +2776,24 @@ void server_options(char **args, int *argc_p)
|
||||
}
|
||||
|
||||
if (backup_dir) {
|
||||
/* This split idiom allows for ~/path expansion via the shell. */
|
||||
args[ac++] = "--backup-dir";
|
||||
args[ac++] = backup_dir;
|
||||
args[ac++] = safe_arg("", backup_dir);
|
||||
}
|
||||
|
||||
/* Only send --suffix if it specifies a non-default value. */
|
||||
if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
|
||||
/* We use the following syntax to avoid weirdness with '~'. */
|
||||
if (asprintf(&arg, "--suffix=%s", backup_suffix) < 0)
|
||||
goto oom;
|
||||
args[ac++] = arg;
|
||||
}
|
||||
if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0)
|
||||
args[ac++] = safe_arg("--suffix", backup_suffix);
|
||||
|
||||
if (checksum_choice) {
|
||||
if (asprintf(&arg, "--checksum-choice=%s", checksum_choice) < 0)
|
||||
goto oom;
|
||||
args[ac++] = arg;
|
||||
}
|
||||
if (checksum_choice)
|
||||
args[ac++] = safe_arg("--checksum-choice", checksum_choice);
|
||||
|
||||
if (do_compression == CPRES_ZLIBX)
|
||||
args[ac++] = "--new-compress";
|
||||
else if (compress_choice && do_compression == CPRES_ZLIB)
|
||||
args[ac++] = "--old-compress";
|
||||
else if (compress_choice) {
|
||||
if (asprintf(&arg, "--compress-choice=%s", compress_choice) < 0)
|
||||
goto oom;
|
||||
args[ac++] = arg;
|
||||
}
|
||||
else if (compress_choice)
|
||||
args[ac++] = safe_arg("--compress-choice", compress_choice);
|
||||
|
||||
if (am_sender) {
|
||||
if (max_delete > 0) {
|
||||
@@ -2693,14 +2802,10 @@ void server_options(char **args, int *argc_p)
|
||||
args[ac++] = arg;
|
||||
} else if (max_delete == 0)
|
||||
args[ac++] = "--max-delete=-1";
|
||||
if (min_size >= 0) {
|
||||
args[ac++] = "--min-size";
|
||||
args[ac++] = min_size_arg;
|
||||
}
|
||||
if (max_size >= 0) {
|
||||
args[ac++] = "--max-size";
|
||||
args[ac++] = max_size_arg;
|
||||
}
|
||||
if (min_size >= 0)
|
||||
args[ac++] = safe_arg("--min-size", min_size_arg);
|
||||
if (max_size >= 0)
|
||||
args[ac++] = safe_arg("--max-size", max_size_arg);
|
||||
if (delete_before)
|
||||
args[ac++] = "--delete-before";
|
||||
else if (delete_during == 2)
|
||||
@@ -2724,17 +2829,12 @@ void server_options(char **args, int *argc_p)
|
||||
if (do_stats)
|
||||
args[ac++] = "--stats";
|
||||
} else {
|
||||
if (skip_compress) {
|
||||
if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0)
|
||||
goto oom;
|
||||
args[ac++] = arg;
|
||||
}
|
||||
if (skip_compress)
|
||||
args[ac++] = safe_arg("--skip-compress", skip_compress);
|
||||
}
|
||||
|
||||
if (max_alloc_arg && max_alloc != DEFAULT_MAX_ALLOC) {
|
||||
args[ac++] = "--max-alloc";
|
||||
args[ac++] = max_alloc_arg;
|
||||
}
|
||||
if (max_alloc_arg && max_alloc != DEFAULT_MAX_ALLOC)
|
||||
args[ac++] = safe_arg("--max-alloc", max_alloc_arg);
|
||||
|
||||
/* --delete-missing-args needs the cooperation of both sides, but
|
||||
* the sender can handle --ignore-missing-args by itself. */
|
||||
@@ -2759,7 +2859,7 @@ void server_options(char **args, int *argc_p)
|
||||
if (partial_dir && am_sender) {
|
||||
if (partial_dir != tmp_partialdir) {
|
||||
args[ac++] = "--partial-dir";
|
||||
args[ac++] = partial_dir;
|
||||
args[ac++] = safe_arg("", partial_dir);
|
||||
}
|
||||
if (delay_updates)
|
||||
args[ac++] = "--delay-updates";
|
||||
@@ -2782,17 +2882,11 @@ void server_options(char **args, int *argc_p)
|
||||
args[ac++] = "--use-qsort";
|
||||
|
||||
if (am_sender) {
|
||||
if (usermap) {
|
||||
if (asprintf(&arg, "--usermap=%s", usermap) < 0)
|
||||
goto oom;
|
||||
args[ac++] = arg;
|
||||
}
|
||||
if (usermap)
|
||||
args[ac++] = safe_arg("--usermap", usermap);
|
||||
|
||||
if (groupmap) {
|
||||
if (asprintf(&arg, "--groupmap=%s", groupmap) < 0)
|
||||
goto oom;
|
||||
args[ac++] = arg;
|
||||
}
|
||||
if (groupmap)
|
||||
args[ac++] = safe_arg("--groupmap", groupmap);
|
||||
|
||||
if (ignore_existing)
|
||||
args[ac++] = "--ignore-existing";
|
||||
@@ -2803,7 +2897,7 @@ void server_options(char **args, int *argc_p)
|
||||
|
||||
if (tmpdir) {
|
||||
args[ac++] = "--temp-dir";
|
||||
args[ac++] = tmpdir;
|
||||
args[ac++] = safe_arg("", tmpdir);
|
||||
}
|
||||
|
||||
if (do_fsync)
|
||||
@@ -2816,7 +2910,7 @@ void server_options(char **args, int *argc_p)
|
||||
*/
|
||||
for (i = 0; i < basis_dir_cnt; i++) {
|
||||
args[ac++] = alt_dest_opt(0);
|
||||
args[ac++] = basis_dir[i];
|
||||
args[ac++] = safe_arg("", basis_dir[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2834,14 +2928,14 @@ void server_options(char **args, int *argc_p)
|
||||
} else if (inplace) {
|
||||
args[ac++] = "--inplace";
|
||||
/* Work around a bug in older rsync versions (on the remote side) for --inplace --sparse */
|
||||
if (sparse_files && !whole_file)
|
||||
if (sparse_files && !whole_file && am_sender)
|
||||
args[ac++] = "--no-W";
|
||||
}
|
||||
|
||||
if (files_from && (!am_sender || filesfrom_host)) {
|
||||
if (filesfrom_host) {
|
||||
args[ac++] = "--files-from";
|
||||
args[ac++] = files_from;
|
||||
args[ac++] = safe_arg("", files_from);
|
||||
if (eol_nulls)
|
||||
args[ac++] = "--from0";
|
||||
} else {
|
||||
@@ -2863,6 +2957,9 @@ void server_options(char **args, int *argc_p)
|
||||
else if (remove_source_files)
|
||||
args[ac++] = "--remove-sent-files";
|
||||
|
||||
if (copy_devices && !am_sender)
|
||||
args[ac++] = "--copy-devices";
|
||||
|
||||
if (preallocate_files && am_sender)
|
||||
args[ac++] = "--preallocate";
|
||||
|
||||
@@ -2884,7 +2981,7 @@ void server_options(char **args, int *argc_p)
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
for (j = 1; j <= remote_option_cnt; j++)
|
||||
args[ac++] = (char*)remote_options[j];
|
||||
args[ac++] = safe_arg(SPLIT_ARG_WHEN_OLD, remote_options[j]);
|
||||
}
|
||||
|
||||
*argc_p = ac;
|
||||
|
||||
@@ -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.2.7
|
||||
%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,8 +79,8 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%dir /etc/rsync-ssl/certs
|
||||
|
||||
%changelog
|
||||
* Sun Jan 02 2022 Wayne Davison <wayne@opencoder.net>
|
||||
Released 3.2.4pre1.
|
||||
* Thu Oct 20 2022 Wayne Davison <wayne@opencoder.net>
|
||||
Released 3.2.7.
|
||||
|
||||
* Fri Mar 21 2008 Wayne Davison <wayne@opencoder.net>
|
||||
Added installation of /etc/xinetd.d/rsync file and some commented-out
|
||||
|
||||
18
packaging/openssl-rsync.cnf
Normal file
18
packaging/openssl-rsync.cnf
Normal file
@@ -0,0 +1,18 @@
|
||||
# This config file can be used with rsync to enable legacy digests
|
||||
# (such as MD4) by using the OPENSSL_CONF environment variable.
|
||||
# See rsync's configure --with-openssl-conf=/path/name option.
|
||||
|
||||
openssl_conf = openssl_init
|
||||
|
||||
[openssl_init]
|
||||
providers = provider_sect
|
||||
|
||||
[provider_sect]
|
||||
default = default_sect
|
||||
legacy = legacy_sect
|
||||
|
||||
[default_sect]
|
||||
activate = 1
|
||||
|
||||
[legacy_sect]
|
||||
activate = 1
|
||||
@@ -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)
|
||||
@@ -176,12 +176,12 @@ def mandate_gensend_hook():
|
||||
print('Creating hook file:', hook)
|
||||
cmd_chk(['./rsync', '-a', 'packaging/pre-push', hook])
|
||||
else:
|
||||
ct = cmd_txt(['fgrep', 'make gensend', hook], discard='output')
|
||||
ct = cmd_txt(['grep', '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 +189,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)
|
||||
|
||||
@@ -105,6 +105,8 @@ def main():
|
||||
if not re.match(r'^del', ans, flags=re.I):
|
||||
die("Aborted")
|
||||
cmd_chk(['git', 'tag', '-d', v_ver])
|
||||
if os.path.isdir('patches/.git'):
|
||||
cmd_chk(f"cd patches && git tag -d '{v_ver}'")
|
||||
|
||||
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
|
||||
if 'pre' in version and not curversion.endswith('dev'):
|
||||
@@ -193,7 +195,9 @@ About to:
|
||||
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'
|
||||
x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M)
|
||||
msg = f"Unable to update RSYNC_VERSION in {fn}"
|
||||
txt = replace_or_die(x_re, r'\1 "%s"' % version, txt, msg)
|
||||
elif '.spec' in fn:
|
||||
for var, val in specvars.items():
|
||||
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
|
||||
@@ -206,10 +210,10 @@ About to:
|
||||
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
|
||||
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'<a name="{finalversion}"></a>\n\n# NEWS for rsync {finalversion} ({rel_day})\n\n'
|
||||
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'
|
||||
@@ -230,7 +234,7 @@ About to:
|
||||
cmd_chk(['packaging/year-tweak'])
|
||||
|
||||
print(dash_line)
|
||||
cmd_run("git diff --color | less -p '^diff .*'")
|
||||
cmd_run("git diff".split())
|
||||
|
||||
srctar_name = f"{rsync_ver}.tar.gz"
|
||||
pattar_name = f"rsync-patches-{version}.tar.gz"
|
||||
@@ -245,20 +249,20 @@ About to:
|
||||
|
||||
About to:
|
||||
- git commit all changes
|
||||
- generate the manpages
|
||||
- run a full build, ensuring that the manpages & configure.sh are up-to-date
|
||||
- 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}'])
|
||||
s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]'])
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
cmd_chk('make gen')
|
||||
cmd_chk('touch configure.ac && packaging/smart-make && make gen')
|
||||
|
||||
print(f'Creating any missing patch branches.')
|
||||
print('Creating any missing patch branches.')
|
||||
s = cmd_run(f'packaging/branch-from-patch --branch={args.master_branch} --add-missing')
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
@@ -341,7 +345,7 @@ About to:
|
||||
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(["./md-convert", "--dest", dest, *md_files])
|
||||
|
||||
cmd_chk(f"git log --name-status | gzip -9 >{dest}/ChangeLog.gz")
|
||||
|
||||
|
||||
@@ -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:
|
||||
#
|
||||
|
||||
@@ -36,9 +36,15 @@ int poptDupArgv(int argc, const char **argv,
|
||||
dst += (argc + 1) * sizeof(*argv);
|
||||
|
||||
/*@-branchstate@*/
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv2[i] = dst;
|
||||
dst += strlcpy(dst, argv[i], nb) + 1;
|
||||
{
|
||||
char * const end_buf = (char *)argv2 + nb;
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv2[i] = dst;
|
||||
/* nb is the TOTAL buffer size, not the remaining bytes; use the
|
||||
* remaining bytes from dst to end_buf so glibc 2.39+ fortified
|
||||
* strlcpy doesn't trip __bos() and abort. */
|
||||
dst += strlcpy(dst, argv[i], end_buf - dst) + 1;
|
||||
}
|
||||
}
|
||||
/*@=branchstate@*/
|
||||
argv2[argc] = NULL;
|
||||
|
||||
@@ -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.
|
||||
|
||||
12
progress.c
12
progress.c
@@ -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;
|
||||
|
||||
150
receiver.c
150
receiver.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996-2000 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
|
||||
@@ -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;
|
||||
@@ -211,7 +215,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 +249,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 +288,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 +318,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 +331,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 +352,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)
|
||||
@@ -393,7 +415,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 +425,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 +456,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 +472,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 +575,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 +612,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 +621,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 +726,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 +744,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
|
||||
@@ -757,25 +794,31 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
fd1 = secure_relative_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_relative_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 +851,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 +879,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_relative_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 +945,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 +954,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 +965,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 +976,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;
|
||||
|
||||
107
rsync-ssl.1.md
107
rsync-ssl.1.md
@@ -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.
|
||||
|
||||
|
||||
2854
rsync.1.md
2854
rsync.1.md
File diff suppressed because it is too large
Load Diff
17
rsync.c
17
rsync.c
@@ -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;
|
||||
|
||||
68
rsync.h
68
rsync.h
@@ -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. */
|
||||
|
||||
@@ -86,6 +84,7 @@
|
||||
#define FLAG_DUPLICATE (1<<4) /* sender */
|
||||
#define FLAG_MISSING_DIR (1<<4) /* generator */
|
||||
#define FLAG_HLINKED (1<<5) /* receiver/generator (checked on all types) */
|
||||
#define FLAG_GOT_DIR_FLIST (1<<5)/* sender/receiver/generator - dir_flist only */
|
||||
#define FLAG_HLINK_FIRST (1<<6) /* receiver/generator (w/FLAG_HLINKED) */
|
||||
#define FLAG_IMPLIED_DIR (1<<6) /* sender/receiver/generator (dirs only) */
|
||||
#define FLAG_HLINK_LAST (1<<7) /* receiver/generator */
|
||||
@@ -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
|
||||
|
||||
272
rsyncd.conf.5.md
272
rsyncd.conf.5.md
@@ -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,7 +71,7 @@ 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
|
||||
@@ -96,9 +98,9 @@ a literal % into a value is to use %%.
|
||||
|
||||
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 +128,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 +138,7 @@ 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
|
||||
## 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
|
||||
@@ -162,6 +164,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 +189,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 +197,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 +247,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 +314,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 +328,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 +354,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 +370,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 +382,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 +411,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 +419,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 +444,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 +456,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 +490,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 +500,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 +519,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 +535,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 +562,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 +590,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 +599,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 +641,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 +653,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 +691,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 +705,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 +744,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 +760,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 +775,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 +783,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 +824,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 +841,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 +922,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 +952,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 +961,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 +974,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.
|
||||
@@ -1038,7 +1069,7 @@ 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
|
||||
## 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 +1124,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 +1139,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 +1177,14 @@ 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
|
||||
## DAEMON CONFIG EXAMPLES
|
||||
|
||||
A simple rsyncd.conf file that allow anonymous rsync to a ftp area at
|
||||
`/home/ftp` would be:
|
||||
@@ -1202,46 +1233,41 @@ 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/WayneD/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 have later contributed to it. It is currently maintained by Wayne
|
||||
Davison.
|
||||
|
||||
Mailing lists for support and development are available at
|
||||
<https://lists.samba.org/>.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2001, 2002 by 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 version
|
||||
@@ -238,7 +238,7 @@ failed=0
|
||||
# failure to aid investigation. We don't remove the testtmp subdir at
|
||||
# the end so that it can be configured as a symlink to a filesystem that
|
||||
# has ACLs and xattr support enabled (if desired).
|
||||
scratchbase="$TOOLDIR"/testtmp
|
||||
scratchbase="${scratchbase:-$TOOLDIR}"/testtmp
|
||||
echo " scratchbase=$scratchbase"
|
||||
[ -d "$scratchbase" ] || mkdir "$scratchbase"
|
||||
|
||||
|
||||
51
sender.c
51
sender.c
@@ -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
|
||||
@@ -25,11 +25,13 @@
|
||||
extern int do_xfers;
|
||||
extern int am_server;
|
||||
extern int am_daemon;
|
||||
extern int local_server;
|
||||
extern int inc_recurse;
|
||||
extern int log_before_transfer;
|
||||
extern int stdout_format_has_i;
|
||||
extern int logfile_format_has_i;
|
||||
extern int want_xattr_optim;
|
||||
extern int xfer_sum_len;
|
||||
extern int csum_length;
|
||||
extern int append_mode;
|
||||
extern int copy_links;
|
||||
@@ -37,6 +39,7 @@ extern int io_error;
|
||||
extern int flist_eof;
|
||||
extern int whole_file;
|
||||
extern int allowed_lull;
|
||||
extern int copy_devices;
|
||||
extern int preserve_xattrs;
|
||||
extern int protocol_version;
|
||||
extern int remove_source_files;
|
||||
@@ -45,11 +48,14 @@ extern int make_backups;
|
||||
extern int inplace;
|
||||
extern int inplace_partial;
|
||||
extern int batch_fd;
|
||||
extern int use_secure_symlinks;
|
||||
extern char *module_dir;
|
||||
extern int write_batch;
|
||||
extern int file_old_total;
|
||||
extern BOOL want_progress_now;
|
||||
extern struct stats stats;
|
||||
extern struct file_list *cur_flist, *first_flist, *dir_flist;
|
||||
extern char num_dev_ino_buf[4 + 8 + 8];
|
||||
|
||||
BOOL extra_flist_sending_enabled;
|
||||
|
||||
@@ -91,10 +97,11 @@ static struct sum_struct *receive_sums(int f)
|
||||
return(s);
|
||||
|
||||
s->sums = new_array(struct sum_buf, s->count);
|
||||
s->sum2_array = new_array(char, (size_t)s->count * xfer_sum_len);
|
||||
|
||||
for (i = 0; i < s->count; i++) {
|
||||
s->sums[i].sum1 = read_int(f);
|
||||
read_buf(f, s->sums[i].sum2, s->s2length);
|
||||
read_buf(f, sum2_at(s, i), s->s2length);
|
||||
|
||||
s->sums[i].offset = offset;
|
||||
s->sums[i].flags = 0;
|
||||
@@ -133,6 +140,8 @@ void successful_send(int ndx)
|
||||
return;
|
||||
|
||||
flist = flist_for_ndx(ndx, "successful_send");
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
file = flist->files[ndx - flist->ndx_start];
|
||||
if (!change_pathname(file, NULL, 0))
|
||||
return;
|
||||
@@ -143,6 +152,13 @@ void successful_send(int ndx)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (local_server
|
||||
&& (int64)st.st_dev == IVAL64(num_dev_ino_buf, 4)
|
||||
&& (int64)st.st_ino == IVAL64(num_dev_ino_buf, 4 + 8)) {
|
||||
rprintf(FERROR_XFER, "ERROR: Skipping sender remove of destination file: %s\n", fname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (st.st_size != F_LENGTH(file) || st.st_mtime != file->modtime
|
||||
#ifdef ST_MTIME_NSEC
|
||||
|| (NSEC_BUMP(file) && (uint32)st.ST_MTIME_NSEC != F_MOD_NSEC(file))
|
||||
@@ -250,6 +266,8 @@ void send_files(int f_in, int f_out)
|
||||
|
||||
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];
|
||||
if (F_PATHNAME(file)) {
|
||||
@@ -338,7 +356,25 @@ void send_files(int f_in, int f_out)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
|
||||
fd = do_open(fname, O_RDONLY, 0);
|
||||
if (use_secure_symlinks) {
|
||||
/* Open from module root to prevent TOCTOU race where
|
||||
* change_pathname's chdir follows a directory symlink.
|
||||
* Reconstruct the full path relative to module_dir
|
||||
* from F_PATHNAME (path) and f_name (fname). */
|
||||
char secure_path[MAXPATHLEN];
|
||||
int slen = snprintf(secure_path, sizeof secure_path, "%s%s%s", path, slash, fname);
|
||||
if (slen >= (int)sizeof secure_path) {
|
||||
io_error |= IOERR_GENERAL;
|
||||
rprintf(FERROR_XFER, "path too long: %s%s%s\n", path, slash, fname);
|
||||
free_sums(s);
|
||||
if (protocol_version >= 30)
|
||||
send_msg_int(MSG_NO_SEND, ndx);
|
||||
continue;
|
||||
}
|
||||
fd = secure_relative_open(module_dir, secure_path, O_RDONLY, 0);
|
||||
} else {
|
||||
fd = do_open_checklinks(fname);
|
||||
}
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
enum logcode c = am_daemon && protocol_version < 28 ? FERROR : FWARNING;
|
||||
@@ -366,6 +402,15 @@ void send_files(int f_in, int f_out)
|
||||
exit_cleanup(RERR_FILEIO);
|
||||
}
|
||||
|
||||
if (IS_DEVICE(st.st_mode)) {
|
||||
if (!copy_devices) {
|
||||
rprintf(FERROR, "attempt to copy device contents without --copy-devices\n");
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (st.st_size == 0)
|
||||
st.st_size = get_device_size(fd, fname);
|
||||
}
|
||||
|
||||
if (append_mode > 0 && st.st_size < F_LENGTH(file)) {
|
||||
rprintf(FWARNING, "skipped diminished file: %s\n",
|
||||
full_fname(fname));
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_ROLL_ASM /* { */
|
||||
|
||||
#define CHAR_OFFSET 0 /* Keep this the same as rsync.h, which isn't likely to change. */
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define get_checksum1_avx2 _get_checksum1_avx2
|
||||
#define get_checksum1_avx2_asm _get_checksum1_avx2_asm
|
||||
#endif
|
||||
|
||||
.intel_syntax noprefix
|
||||
.text
|
||||
|
||||
.p2align 5
|
||||
.globl get_checksum1_avx2
|
||||
.globl get_checksum1_avx2_asm
|
||||
|
||||
# rdi=*buf, esi=len, edx=i, rcx= *ps1, r8= *ps2
|
||||
get_checksum1_avx2:
|
||||
get_checksum1_avx2_asm:
|
||||
vmovd xmm6,[rcx] # load *ps1
|
||||
lea eax, [rsi-128] # at least 128 bytes to process?
|
||||
cmp edx, eax
|
||||
@@ -167,3 +173,5 @@ get_checksum1_avx2:
|
||||
.byte 3
|
||||
.byte 2
|
||||
.byte 1
|
||||
|
||||
#endif /* } USE_ROLL_ASM */
|
||||
|
||||
@@ -51,12 +51,12 @@
|
||||
* GCC 4.x are not supported to ease configure.ac logic.
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
#ifdef __cplusplus
|
||||
#ifdef __x86_64__ /* { */
|
||||
#ifdef __cplusplus /* { */
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
#ifdef HAVE_SIMD
|
||||
#ifdef USE_ROLL_SIMD /* { */
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
@@ -85,6 +85,9 @@ typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, _
|
||||
#define SSE2_HADDS_EPI16(a, b) _mm_adds_epi16(SSE2_INTERLEAVE_EVEN_EPI16(a, b), SSE2_INTERLEAVE_ODD_EPI16(a, b))
|
||||
#define SSE2_MADDUBS_EPI16(a, b) _mm_adds_epi16(SSE2_MULU_EVEN_EPI8(a, b), SSE2_MULU_ODD_EPI8(a, b))
|
||||
|
||||
#ifndef USE_ROLL_ASM
|
||||
__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
|
||||
#endif
|
||||
__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_ssse3_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
|
||||
__attribute__ ((target("default"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2) { return i; }
|
||||
|
||||
@@ -245,7 +248,7 @@ __attribute__ ((target("sse2"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf
|
||||
|
||||
// (4*buf[i] + 3*buf[i+1]), (2*buf[i+2], buf[i+3]), ... 2*[int16*8]
|
||||
__m128i mul_const = _mm_set1_epi32(4 + (3 << 8) + (2 << 16) + (1 << 24));
|
||||
__m128i mul_add16_1 = SSE2_MADDUBS_EPI16(mul_const, in8_1);
|
||||
__m128i mul_add16_1 = SSE2_MADDUBS_EPI16(mul_const, in8_1);
|
||||
__m128i mul_add16_2 = SSE2_MADDUBS_EPI16(mul_const, in8_2);
|
||||
|
||||
// s2 += 32*s1
|
||||
@@ -310,7 +313,126 @@ __attribute__ ((target("sse2"))) MVSTATIC int32 get_checksum1_sse2_32(schar* buf
|
||||
return i;
|
||||
}
|
||||
|
||||
extern "C" __attribute__ ((target("avx2"))) int32 get_checksum1_avx2(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2);
|
||||
#ifdef USE_ROLL_ASM /* { */
|
||||
|
||||
extern "C" __attribute__ ((target("avx2"))) int32 get_checksum1_avx2_asm(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2);
|
||||
|
||||
#else /* } { */
|
||||
|
||||
/*
|
||||
AVX2 loop per 64 bytes:
|
||||
int16 t1[16];
|
||||
int16 t2[16];
|
||||
for (int j = 0; j < 16; j++) {
|
||||
t1[j] = buf[j*4 + i] + buf[j*4 + i+1] + buf[j*4 + i+2] + buf[j*4 + i+3];
|
||||
t2[j] = 4*buf[j*4 + i] + 3*buf[j*4 + i+1] + 2*buf[j*4 + i+2] + buf[j*4 + i+3];
|
||||
}
|
||||
s2 += 64*s1 + (uint32)(
|
||||
60*t1[0] + 56*t1[1] + 52*t1[2] + 48*t1[3] + 44*t1[4] + 40*t1[5] + 36*t1[6] + 32*t1[7] + 28*t1[8] + 24*t1[9] + 20*t1[10] + 16*t1[11] + 12*t1[12] + 8*t1[13] + 4*t1[14] +
|
||||
t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7] + t2[8] + t2[9] + t2[10] + t2[11] + t2[12] + t2[13] + t2[14] + t2[15]
|
||||
) + 2080*CHAR_OFFSET;
|
||||
s1 += (uint32)(t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7] + t1[8] + t1[9] + t1[10] + t1[11] + t1[12] + t1[13] + t1[14] + t1[15]) +
|
||||
64*CHAR_OFFSET;
|
||||
*/
|
||||
|
||||
__attribute__ ((target("avx2"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
|
||||
{
|
||||
if (len > 64) {
|
||||
|
||||
uint32 x[4] = {0};
|
||||
__m128i ss1 = _mm_cvtsi32_si128(*ps1);
|
||||
__m128i ss2 = _mm_cvtsi32_si128(*ps2);
|
||||
|
||||
const char mul_t1_buf[16] = {60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0};
|
||||
__m128i tmp = _mm_load_si128((__m128i*) mul_t1_buf);
|
||||
__m256i mul_t1 = _mm256_cvtepu8_epi16(tmp);
|
||||
__m256i mul_const = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(4 | (3 << 8) | (2 << 16) | (1 << 24)));
|
||||
__m256i mul_one = _mm256_set1_epi8(1);
|
||||
|
||||
for (; i < (len-64); i+=64) {
|
||||
// Load ... 4*[int8*16]
|
||||
__m256i in8_1, in8_2;
|
||||
__m128i in8_1_low, in8_2_low, in8_1_high, in8_2_high;
|
||||
in8_1_low = _mm_loadu_si128((__m128i_u*)&buf[i]);
|
||||
in8_2_low = _mm_loadu_si128((__m128i_u*)&buf[i+16]);
|
||||
in8_1_high = _mm_loadu_si128((__m128i_u*)&buf[i+32]);
|
||||
in8_2_high = _mm_loadu_si128((__m128i_u*)&buf[i+48]);
|
||||
in8_1 = _mm256_inserti128_si256(_mm256_castsi128_si256(in8_1_low), in8_1_high,1);
|
||||
in8_2 = _mm256_inserti128_si256(_mm256_castsi128_si256(in8_2_low), in8_2_high,1);
|
||||
|
||||
// (1*buf[i] + 1*buf[i+1]), (1*buf[i+2], 1*buf[i+3]), ... 2*[int16*8]
|
||||
// Fastest, even though multiply by 1
|
||||
__m256i add16_1 = _mm256_maddubs_epi16(mul_one, in8_1);
|
||||
__m256i add16_2 = _mm256_maddubs_epi16(mul_one, in8_2);
|
||||
|
||||
// (4*buf[i] + 3*buf[i+1]), (2*buf[i+2], buf[i+3]), ... 2*[int16*8]
|
||||
__m256i mul_add16_1 = _mm256_maddubs_epi16(mul_const, in8_1);
|
||||
__m256i mul_add16_2 = _mm256_maddubs_epi16(mul_const, in8_2);
|
||||
|
||||
// s2 += 64*s1
|
||||
ss2 = _mm_add_epi32(ss2, _mm_slli_epi32(ss1, 6));
|
||||
|
||||
// [sum(t1[0]..t1[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
|
||||
__m256i sum_add32 = _mm256_add_epi16(add16_1, add16_2);
|
||||
sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_epi32(sum_add32, 16));
|
||||
sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_si256(sum_add32, 4));
|
||||
sum_add32 = _mm256_add_epi16(sum_add32, _mm256_srli_si256(sum_add32, 8));
|
||||
|
||||
// [sum(t2[0]..t2[7]), X, X, X] [int32*4]; faster than multiple _mm_hadds_epi16
|
||||
__m256i sum_mul_add32 = _mm256_add_epi16(mul_add16_1, mul_add16_2);
|
||||
sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_epi32(sum_mul_add32, 16));
|
||||
sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_si256(sum_mul_add32, 4));
|
||||
sum_mul_add32 = _mm256_add_epi16(sum_mul_add32, _mm256_srli_si256(sum_mul_add32, 8));
|
||||
|
||||
// s1 += t1[0] + t1[1] + t1[2] + t1[3] + t1[4] + t1[5] + t1[6] + t1[7]
|
||||
__m128i sum_add32_hi = _mm256_extracti128_si256(sum_add32, 0x1);
|
||||
ss1 = _mm_add_epi32(ss1, _mm256_castsi256_si128(sum_add32));
|
||||
ss1 = _mm_add_epi32(ss1, sum_add32_hi);
|
||||
|
||||
// s2 += t2[0] + t2[1] + t2[2] + t2[3] + t2[4] + t2[5] + t2[6] + t2[7]
|
||||
__m128i sum_mul_add32_hi = _mm256_extracti128_si256(sum_mul_add32, 0x1);
|
||||
ss2 = _mm_add_epi32(ss2, _mm256_castsi256_si128(sum_mul_add32));
|
||||
ss2 = _mm_add_epi32(ss2, sum_mul_add32_hi);
|
||||
|
||||
// [t1[0] + t1[1], t1[2] + t1[3] ...] [int16*8]
|
||||
// We could've combined this with generating sum_add32 above and
|
||||
// save an instruction but benchmarking shows that as being slower
|
||||
__m256i add16 = _mm256_hadds_epi16(add16_1, add16_2);
|
||||
|
||||
// [t1[0], t1[1], ...] -> [t1[0]*28 + t1[1]*24, ...] [int32*4]
|
||||
__m256i mul32 = _mm256_madd_epi16(add16, mul_t1);
|
||||
|
||||
// [sum(mul32), X, X, X] [int32*4]; faster than multiple _mm_hadd_epi32
|
||||
mul32 = _mm256_add_epi32(mul32, _mm256_srli_si256(mul32, 4));
|
||||
mul32 = _mm256_add_epi32(mul32, _mm256_srli_si256(mul32, 8));
|
||||
// prefetch 2 cacheline ahead
|
||||
_mm_prefetch(&buf[i + 160], _MM_HINT_T0);
|
||||
|
||||
// s2 += 28*t1[0] + 24*t1[1] + 20*t1[2] + 16*t1[3] + 12*t1[4] + 8*t1[5] + 4*t1[6]
|
||||
__m128i mul32_hi = _mm256_extracti128_si256(mul32, 0x1);
|
||||
ss2 = _mm_add_epi32(ss2, _mm256_castsi256_si128(mul32));
|
||||
ss2 = _mm_add_epi32(ss2, mul32_hi);
|
||||
|
||||
#if CHAR_OFFSET != 0
|
||||
// s1 += 32*CHAR_OFFSET
|
||||
__m128i char_offset_multiplier = _mm_set1_epi32(32 * CHAR_OFFSET);
|
||||
ss1 = _mm_add_epi32(ss1, char_offset_multiplier);
|
||||
|
||||
// s2 += 528*CHAR_OFFSET
|
||||
char_offset_multiplier = _mm_set1_epi32(528 * CHAR_OFFSET);
|
||||
ss2 = _mm_add_epi32(ss2, char_offset_multiplier);
|
||||
#endif
|
||||
}
|
||||
|
||||
_mm_store_si128((__m128i_u*)x, ss1);
|
||||
*ps1 = x[0];
|
||||
_mm_store_si128((__m128i_u*)x, ss2);
|
||||
*ps2 = x[0];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
#endif /* } !USE_ROLL_ASM */
|
||||
|
||||
static int32 get_checksum1_default_1(schar* buf, int32 len, int32 i, uint32* ps1, uint32* ps2)
|
||||
{
|
||||
@@ -338,7 +460,11 @@ static inline uint32 get_checksum1_cpp(char *buf1, int32 len)
|
||||
uint32 s2 = 0;
|
||||
|
||||
// multiples of 64 bytes using AVX2 (if available)
|
||||
i = get_checksum1_avx2((schar*)buf1, len, i, &s1, &s2);
|
||||
#ifdef USE_ROLL_ASM
|
||||
i = get_checksum1_avx2_asm((schar*)buf1, len, i, &s1, &s2);
|
||||
#else
|
||||
i = get_checksum1_avx2_64((schar*)buf1, len, i, &s1, &s2);
|
||||
#endif
|
||||
|
||||
// multiples of 32 bytes using SSSE3 (if available)
|
||||
i = get_checksum1_ssse3_32((schar*)buf1, len, i, &s1, &s2);
|
||||
@@ -407,7 +533,11 @@ int main() {
|
||||
benchmark("Raw-C", get_checksum1_default_1, (schar*)buf, BLOCK_LEN);
|
||||
benchmark("SSE2", get_checksum1_sse2_32, (schar*)buf, BLOCK_LEN);
|
||||
benchmark("SSSE3", get_checksum1_ssse3_32, (schar*)buf, BLOCK_LEN);
|
||||
benchmark("AVX2", get_checksum1_avx2, (schar*)buf, BLOCK_LEN);
|
||||
#ifdef USE_ROLL_ASM
|
||||
benchmark("AVX2-ASM", get_checksum1_avx2_asm, (schar*)buf, BLOCK_LEN);
|
||||
#else
|
||||
benchmark("AVX2", get_checksum1_avx2_64, (schar*)buf, BLOCK_LEN);
|
||||
#endif
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
@@ -417,6 +547,118 @@ int main() {
|
||||
#pragma clang optimize on
|
||||
#endif /* BENCHMARK_SIMD_CHECKSUM1 */
|
||||
|
||||
#endif /* HAVE_SIMD */
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __x86_64__ */
|
||||
#ifdef TEST_SIMD_CHECKSUM1
|
||||
|
||||
static uint32 checksum_via_default(char *buf, int32 len)
|
||||
{
|
||||
uint32 s1 = 0, s2 = 0;
|
||||
get_checksum1_default_1((schar*)buf, len, 0, &s1, &s2);
|
||||
return (s1 & 0xffff) + (s2 << 16);
|
||||
}
|
||||
|
||||
static uint32 checksum_via_sse2(char *buf, int32 len)
|
||||
{
|
||||
int32 i;
|
||||
uint32 s1 = 0, s2 = 0;
|
||||
i = get_checksum1_sse2_32((schar*)buf, len, 0, &s1, &s2);
|
||||
get_checksum1_default_1((schar*)buf, len, i, &s1, &s2);
|
||||
return (s1 & 0xffff) + (s2 << 16);
|
||||
}
|
||||
|
||||
static uint32 checksum_via_ssse3(char *buf, int32 len)
|
||||
{
|
||||
int32 i;
|
||||
uint32 s1 = 0, s2 = 0;
|
||||
i = get_checksum1_ssse3_32((schar*)buf, len, 0, &s1, &s2);
|
||||
get_checksum1_default_1((schar*)buf, len, i, &s1, &s2);
|
||||
return (s1 & 0xffff) + (s2 << 16);
|
||||
}
|
||||
|
||||
static uint32 checksum_via_avx2(char *buf, int32 len)
|
||||
{
|
||||
int32 i;
|
||||
uint32 s1 = 0, s2 = 0;
|
||||
#ifdef USE_ROLL_ASM
|
||||
i = get_checksum1_avx2_asm((schar*)buf, len, 0, &s1, &s2);
|
||||
#else
|
||||
i = get_checksum1_avx2_64((schar*)buf, len, 0, &s1, &s2);
|
||||
#endif
|
||||
get_checksum1_default_1((schar*)buf, len, i, &s1, &s2);
|
||||
return (s1 & 0xffff) + (s2 << 16);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
static const int sizes[] = {1, 4, 31, 32, 33, 63, 64, 65, 128, 129, 256, 700, 1024, 4096, 65536};
|
||||
int num_sizes = sizeof(sizes) / sizeof(sizes[0]);
|
||||
int max_size = sizes[num_sizes - 1];
|
||||
int failures = 0;
|
||||
|
||||
/* Allocate with extra bytes for unaligned test */
|
||||
unsigned char *raw = (unsigned char *)malloc(max_size + 64 + 1);
|
||||
if (!raw) {
|
||||
fprintf(stderr, "malloc failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Fill with deterministic data */
|
||||
for (int i = 0; i < max_size + 64 + 1; i++)
|
||||
raw[i] = (i + (i % 3) + (i % 11)) % 256;
|
||||
|
||||
/* Test with aligned buffer (64-byte aligned) */
|
||||
unsigned char *aligned = raw + (64 - ((uintptr_t)raw % 64));
|
||||
|
||||
/* Test with unaligned buffer (+1 byte offset) */
|
||||
unsigned char *unaligned = aligned + 1;
|
||||
|
||||
struct { const char *name; unsigned char *buf; } buffers[] = {
|
||||
{"aligned", aligned},
|
||||
{"unaligned", unaligned},
|
||||
};
|
||||
|
||||
for (int b = 0; b < 2; b++) {
|
||||
char *buf = (char *)buffers[b].buf;
|
||||
const char *bname = buffers[b].name;
|
||||
|
||||
for (int s = 0; s < num_sizes; s++) {
|
||||
int32 len = sizes[s];
|
||||
uint32 ref = checksum_via_default(buf, len);
|
||||
uint32 cs_sse2 = checksum_via_sse2(buf, len);
|
||||
uint32 cs_ssse3 = checksum_via_ssse3(buf, len);
|
||||
uint32 cs_avx2 = checksum_via_avx2(buf, len);
|
||||
uint32 cs_auto = get_checksum1(buf, len);
|
||||
|
||||
if (cs_sse2 != ref) {
|
||||
printf("FAIL %-9s size=%5d: SSE2=%08x ref=%08x\n", bname, len, cs_sse2, ref);
|
||||
failures++;
|
||||
}
|
||||
if (cs_ssse3 != ref) {
|
||||
printf("FAIL %-9s size=%5d: SSSE3=%08x ref=%08x\n", bname, len, cs_ssse3, ref);
|
||||
failures++;
|
||||
}
|
||||
if (cs_avx2 != ref) {
|
||||
printf("FAIL %-9s size=%5d: AVX2=%08x ref=%08x\n", bname, len, cs_avx2, ref);
|
||||
failures++;
|
||||
}
|
||||
if (cs_auto != ref) {
|
||||
printf("FAIL %-9s size=%5d: auto=%08x ref=%08x\n", bname, len, cs_auto, ref);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(raw);
|
||||
|
||||
if (failures) {
|
||||
printf("%d checksum mismatches!\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("All SIMD checksum tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST_SIMD_CHECKSUM1 */
|
||||
|
||||
#endif /* } USE_ROLL_SIMD */
|
||||
#endif /* } __cplusplus */
|
||||
#endif /* } __x86_64__ */
|
||||
|
||||
30
socket.c
30
socket.c
@@ -47,21 +47,23 @@ static struct sigaction sigact;
|
||||
|
||||
static int sock_exec(const char *prog);
|
||||
|
||||
#define PROXY_BUF_SIZE 1024
|
||||
|
||||
/* Establish a proxy connection on an open socket to a web proxy by using the
|
||||
* CONNECT method. If proxy_user and proxy_pass are not NULL, they are used to
|
||||
* authenticate to the proxy using the "Basic" proxy-authorization protocol. */
|
||||
static int establish_proxy_connection(int fd, char *host, int port, char *proxy_user, char *proxy_pass)
|
||||
{
|
||||
char *cp, buffer[1024];
|
||||
char *authhdr, authbuf[1024];
|
||||
char *cp, buffer[PROXY_BUF_SIZE + 1];
|
||||
char *authhdr, authbuf[PROXY_BUF_SIZE + 1];
|
||||
int len;
|
||||
|
||||
if (proxy_user && proxy_pass) {
|
||||
stringjoin(buffer, sizeof buffer,
|
||||
stringjoin(buffer, PROXY_BUF_SIZE,
|
||||
proxy_user, ":", proxy_pass, NULL);
|
||||
len = strlen(buffer);
|
||||
|
||||
if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
|
||||
if ((len*8 + 5) / 6 >= PROXY_BUF_SIZE - 3) {
|
||||
rprintf(FERROR,
|
||||
"authentication information is too long\n");
|
||||
return -1;
|
||||
@@ -74,14 +76,14 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
|
||||
authhdr = "";
|
||||
}
|
||||
|
||||
len = snprintf(buffer, sizeof buffer, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
|
||||
assert(len > 0 && len < (int)sizeof buffer);
|
||||
len = snprintf(buffer, PROXY_BUF_SIZE, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
|
||||
assert(len > 0 && len < PROXY_BUF_SIZE);
|
||||
if (write(fd, buffer, len) != len) {
|
||||
rsyserr(FERROR, errno, "failed to write to proxy");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
|
||||
for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE - 1]; cp++) {
|
||||
if (read(fd, cp, 1) != 1) {
|
||||
rsyserr(FERROR, errno, "failed to read from proxy");
|
||||
return -1;
|
||||
@@ -90,11 +92,13 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
|
||||
break;
|
||||
}
|
||||
|
||||
if (*cp != '\n')
|
||||
cp++;
|
||||
*cp-- = '\0';
|
||||
if (*cp == '\r')
|
||||
*cp = '\0';
|
||||
if (cp == &buffer[PROXY_BUF_SIZE - 1]) {
|
||||
rprintf(FERROR, "proxy response line too long\n");
|
||||
return -1;
|
||||
}
|
||||
*cp = '\0';
|
||||
if (cp > buffer && cp[-1] == '\r')
|
||||
cp[-1] = '\0';
|
||||
if (strncmp(buffer, "HTTP/", 5) != 0) {
|
||||
rprintf(FERROR, "bad response from proxy -- %s\n",
|
||||
buffer);
|
||||
@@ -110,7 +114,7 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
|
||||
}
|
||||
/* throw away the rest of the HTTP header */
|
||||
while (1) {
|
||||
for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
|
||||
for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE]; cp++) {
|
||||
if (read(fd, cp, 1) != 1) {
|
||||
rsyserr(FERROR, errno,
|
||||
"failed to read from proxy");
|
||||
|
||||
@@ -38,7 +38,7 @@ def main():
|
||||
print_line(fn, mtime, mtime)
|
||||
ls.discard(fn)
|
||||
|
||||
cmd = git + 'log -r --name-only --no-color --pretty=raw --no-renames -z'.split()
|
||||
cmd = git + 'log -r --name-only --format=%x00commit%x20%H%n%x00commit_time%x20%ct%n --no-renames -z'.split()
|
||||
if args.tree:
|
||||
cmd.append(args.tree)
|
||||
cmd += ['--'] + args.files
|
||||
@@ -46,7 +46,7 @@ def main():
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding='utf-8')
|
||||
for line in proc.stdout:
|
||||
line = line.strip()
|
||||
m = re.match(r'^committer .*? (\d+) [-+]\d+$', line)
|
||||
m = re.match(r'^\0commit_time (\d+)$', line)
|
||||
if m:
|
||||
commit_time = int(m[1])
|
||||
elif NULL_COMMIT_RE.search(line):
|
||||
|
||||
93
support/json-rsync-version
Executable file
93
support/json-rsync-version
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import sys, argparse, subprocess, json
|
||||
|
||||
TWEAK_NAME = {
|
||||
'asm': 'asm_roll',
|
||||
'ASM': 'asm_roll',
|
||||
'hardlink_special': 'hardlink_specials',
|
||||
'protect_args': 'secluded_args',
|
||||
'protected_args': 'secluded_args',
|
||||
'SIMD': 'SIMD_roll',
|
||||
}
|
||||
|
||||
MOVE_OPTIM = set('asm_roll SIMD_roll'.split())
|
||||
|
||||
def main():
|
||||
if not args.rsync or args.rsync == '-':
|
||||
ver_out = sys.stdin.read().strip()
|
||||
else:
|
||||
ver_out = subprocess.check_output([args.rsync, '--version', '--version'], encoding='utf-8').strip()
|
||||
if ver_out.startswith('{'):
|
||||
print(ver_out)
|
||||
return
|
||||
info = { }
|
||||
misplaced_optims = { }
|
||||
for line in ver_out.splitlines():
|
||||
if line.startswith('rsync '):
|
||||
prog, vstr, ver, pstr, vstr2, proto = line.split()
|
||||
info['program'] = prog
|
||||
if ver.startswith('v'):
|
||||
ver = ver[1:]
|
||||
info[vstr] = ver
|
||||
if '.' not in proto:
|
||||
proto += '.0'
|
||||
else:
|
||||
proto = proto.replace('.PR', '.')
|
||||
info[pstr] = proto
|
||||
elif line.startswith('Copyright '):
|
||||
info['copyright'] = line[10:]
|
||||
elif line.startswith('Web site: '):
|
||||
info['url'] = line[10:]
|
||||
elif line.startswith(' '):
|
||||
if not saw_comma and ',' in line:
|
||||
saw_comma = True
|
||||
info[sect_name] = { }
|
||||
if saw_comma:
|
||||
for x in line.strip(' ,').split(', '):
|
||||
if ' ' in x:
|
||||
val, var = x.split(' ', 1)
|
||||
if val == 'no':
|
||||
val = False
|
||||
elif val.endswith('-bit'):
|
||||
var = var[:-1] + '_bits'
|
||||
val = int(val.split('-')[0])
|
||||
else:
|
||||
var = x
|
||||
val = True
|
||||
var = var.replace(' ', '_').replace('-', '_')
|
||||
if var in TWEAK_NAME:
|
||||
var = TWEAK_NAME[var]
|
||||
if sect_name[0] != 'o' and var in MOVE_OPTIM:
|
||||
misplaced_optims[var] = val
|
||||
else:
|
||||
info[sect_name][var] = val
|
||||
else:
|
||||
info[sect_name] += [ x for x in line.split() if not x.startswith('(') ]
|
||||
elif line == '':
|
||||
break
|
||||
else:
|
||||
sect_name = line.strip(' :').replace(' ', '_').lower()
|
||||
info[sect_name] = [ ]
|
||||
saw_comma = False
|
||||
for chk in 'capabilities optimizations'.split():
|
||||
if chk not in info:
|
||||
info[chk] = { }
|
||||
if misplaced_optims:
|
||||
info['optimizations'].update(misplaced_optims)
|
||||
for chk in 'checksum_list compress_list daemon_auth_list'.split():
|
||||
if chk not in info:
|
||||
info[chk] = [ ]
|
||||
info['license'] = 'GPLv3' if ver[0] == '3' else 'GPLv2'
|
||||
info['caveat'] = 'rsync comes with ABSOLUTELY NO WARRANTY'
|
||||
print(json.dumps(info))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Output rsync's version data in JSON format, even if the rsync doesn't support a native json-output method.", add_help=False)
|
||||
parser.add_argument('rsync', nargs='?', help="Specify an rsync command to run. Otherwise stdin is consumed.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et
|
||||
@@ -4,7 +4,8 @@
|
||||
# for testing or for running a local copy where the sender and the
|
||||
# receiver needs to use different options (e.g. --fake-super). If
|
||||
# we get a -l USER option, we try to use "sudo -u USER" to run the
|
||||
# command.
|
||||
# command. Supports only the hostnames "localhost" and "lh", with
|
||||
# the latter implying the --no-cd option.
|
||||
|
||||
user=''
|
||||
do_cd=y # Default path is user's home dir (just like ssh) unless host is "lh".
|
||||
|
||||
@@ -21,7 +21,7 @@ LOGFILE = 'rrsync.log' # NOTE: the file must exist for a line to be appended!
|
||||
|
||||
# NOTE when disabling: check for both a short & long version of the option!
|
||||
|
||||
### 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:
|
||||
short_disabled = 's'
|
||||
@@ -47,6 +47,7 @@ long_opts = {
|
||||
'compress-choice': 1,
|
||||
'compress-level': 1,
|
||||
'copy-dest': 2,
|
||||
'copy-devices': -1,
|
||||
'copy-unsafe-links': 0,
|
||||
'daemon': -1,
|
||||
'debug': 1,
|
||||
@@ -63,7 +64,7 @@ long_opts = {
|
||||
'files-from': 3,
|
||||
'force': 0,
|
||||
'from0': 0,
|
||||
'fsync': 2,
|
||||
'fsync': 0,
|
||||
'fuzzy': 0,
|
||||
'group': 0,
|
||||
'groupmap': 1,
|
||||
@@ -127,7 +128,7 @@ long_opts = {
|
||||
'write-devices': -1,
|
||||
}
|
||||
|
||||
### END of options data produced by the cull_options script. ###
|
||||
### END of options data produced by the cull-options script. ###
|
||||
|
||||
import os, sys, re, argparse, glob, socket, time, subprocess
|
||||
from argparse import RawTextHelpFormatter
|
||||
@@ -360,9 +361,9 @@ if __name__ == '__main__':
|
||||
only_group = arg_parser.add_mutually_exclusive_group()
|
||||
only_group.add_argument('-ro', action='store_true', help="Allow only reading from the DIR. Implies -no-del and -no-lock.")
|
||||
only_group.add_argument('-wo', action='store_true', help="Allow only writing to the DIR.")
|
||||
arg_parser.add_argument('-munge', action='store_true', help="Enable rsync's --munge-links on the server side.")
|
||||
arg_parser.add_argument('-no-del', action='store_true', help="Disable rsync's --delete* and --remove* options.")
|
||||
arg_parser.add_argument('-no-lock', action='store_true', help="Avoid the single-run (per-user) lock check.")
|
||||
arg_parser.add_argument('-munge', action='store_true', help="Enable rsync's --munge-links on the server side.")
|
||||
arg_parser.add_argument('-help', '-h', action='help', help="Output this help message and exit.")
|
||||
arg_parser.add_argument('dir', metavar='DIR', help="The restricted directory to use.")
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
# NAME
|
||||
## NAME
|
||||
|
||||
rrsync - a script to setup restricted rsync users via ssh logins
|
||||
|
||||
# SYNOPSIS
|
||||
## SYNOPSIS
|
||||
|
||||
```
|
||||
rrsync [-ro|-rw] [-munge] [-no-del] DIR
|
||||
rrsync [-ro|-rw] [-munge] [-no-del] [-no-lock] DIR
|
||||
```
|
||||
|
||||
# DESCRIPTION
|
||||
The single non-option argument specifies the restricted _DIR_ to use. It can be
|
||||
relative to the user's home directory or an absolute path.
|
||||
|
||||
The online version of this manpage (that includes cross-linking of topics)
|
||||
is available at <https://download.samba.org/pub/rsync/rrsync.1>.
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
A user's ssh login can be restricted to only allow the running of an rsync
|
||||
transfer in one of two easy ways: forcing the running of the rrsync script
|
||||
or forcing the running of an rsync daemon-over-ssh command.
|
||||
transfer in one of two easy ways:
|
||||
|
||||
* forcing the running of the rrsync script
|
||||
* forcing the running of an rsync daemon-over-ssh command.
|
||||
|
||||
Both of these setups use a feature of ssh that allows a command to be forced to
|
||||
run instead of an interactive shell. However, if the user's home shell is bash,
|
||||
please see [BASH SECURITY ISSUE](#) for a potential issue.
|
||||
|
||||
To use the rrsync script, edit the user's `~/.ssh/authorized_keys` file and add
|
||||
a prefix like one of the following (followed by a space) in front of each
|
||||
@@ -27,7 +39,7 @@ ssh-key line that should be restricted:
|
||||
Then, ensure that the rrsync script has your desired option restrictions. You
|
||||
may want to copy the script to a local bin dir with a unique name if you want
|
||||
to have multiple configurations. One or more rrsync options can be specified
|
||||
prior to the `DIR` if you want to further restrict the transfer.
|
||||
prior to the _DIR_ if you want to further restrict the transfer.
|
||||
|
||||
To use an rsync daemon setup, edit the user's `~/.ssh/authorized_keys` file and
|
||||
add a prefix like one of the following (followed by a space) in front of each
|
||||
@@ -39,41 +51,57 @@ ssh-key line that should be restricted:
|
||||
> ```
|
||||
|
||||
Then, ensure that the rsyncd.conf file is created with one or more module names
|
||||
with the appropriate path and option restrictions. If the `--config` option is
|
||||
omitted, it defaults to `~/rsyncd.conf`. See the `rsyncd.conf` man page for
|
||||
details of how to configure an rsync daemon.
|
||||
with the appropriate path and option restrictions. If rsync's
|
||||
[`--config`](rsync.1#dopt) option is omitted, it defaults to `~/rsyncd.conf`.
|
||||
See the [**rsyncd.conf**(5)](rsyncd.conf.5) manpage for details of how to
|
||||
configure an rsync daemon.
|
||||
|
||||
When using rrsync, there can be just one restricted dir per authorized key. A
|
||||
daemon setup, on the other hand, allows multiple module names inside the config
|
||||
file, each one with its own path setting.
|
||||
|
||||
The remainder of this man page is dedicated to using the rrsync script.
|
||||
The remainder of this manpage is dedicated to using the rrsync script.
|
||||
|
||||
# OPTION SUMMARY
|
||||
## OPTIONS
|
||||
|
||||
```
|
||||
-ro Allow only reading from the DIR. Implies -no-del.
|
||||
-wo Allow only writing to the DIR.
|
||||
-no-del Disable rsync's --delete* and --remove* options.
|
||||
-munge Enable rsync's --munge-links on the server side.
|
||||
-help, -h Output this help message and exit.
|
||||
```
|
||||
0. `-ro`
|
||||
|
||||
A single non-option argument specifies the restricted DIR to use. It can be
|
||||
relative to the user's home directory or an absolute path.
|
||||
Allow only reading from the DIR. Implies [`-no-del`](#opt) and
|
||||
[`-no-lock`](#opt).
|
||||
|
||||
# SECURITY RESTRICTIONS
|
||||
0. `-wo`
|
||||
|
||||
Allow only writing to the DIR.
|
||||
|
||||
0. `-munge`
|
||||
|
||||
Enable rsync's [`--munge-links`](rsync.1#opt) on the server side.
|
||||
|
||||
0. `-no-del`
|
||||
|
||||
Disable rsync's `--delete*` and `--remove*` options.
|
||||
|
||||
0. `-no-lock`
|
||||
|
||||
Avoid the single-run (per-user) lock check. Useful with [`-munge`](#opt).
|
||||
|
||||
0. `-help`, `-h`
|
||||
|
||||
Output this help message and exit.
|
||||
|
||||
## SECURITY RESTRICTIONS
|
||||
|
||||
The rrsync script validates the path arguments it is sent to try to restrict
|
||||
them to staying within the specified DIR.
|
||||
|
||||
The rrsync script rejects rsync's `--copy-links`` option (by default) so that a
|
||||
copy cannot dereference a symlink within the DIR to get to a file outside the
|
||||
DIR.
|
||||
The rrsync script rejects rsync's [`--copy-links`](rsync.1#opt) option (by
|
||||
default) so that a copy cannot dereference a symlink within the DIR to get to a
|
||||
file outside the DIR.
|
||||
|
||||
The rrsync script rejects rsync's `--protect-args` (`-s`) option because it
|
||||
would allow options to be sent to the server-side that the script could not
|
||||
check. If you want to support `--protect-args`, use a daemon-over-ssh setup.
|
||||
The rrsync script rejects rsync's [`--protect-args`](rsync.1#opt) (`-s`) option
|
||||
because it would allow options to be sent to the server-side that the script
|
||||
cannot check. If you want to support `--protect-args`, use a daemon-over-ssh
|
||||
setup.
|
||||
|
||||
The rrsync script accepts just a subset of rsync's options that the real rsync
|
||||
uses when running the server command. A few extra convenience options are also
|
||||
@@ -83,11 +111,56 @@ overrides.
|
||||
The script (or a copy of it) can be manually edited if you want it to customize
|
||||
the option handling.
|
||||
|
||||
# EXAMPLES
|
||||
## BASH SECURITY ISSUE
|
||||
|
||||
The `.ssh/authorized_keys` file might have lines in it like this:
|
||||
If your users have bash set as their home shell, bash may try to be overly
|
||||
helpful and ensure that the user's login bashrc files are run prior to
|
||||
executing the forced command. This can be a problem if the user can somehow
|
||||
update their home bashrc files, perhaps via the restricted copy, a shared home
|
||||
directory, or something similar.
|
||||
|
||||
One simple way to avoid the issue is to switch the user to a simpler shell,
|
||||
such as dash. When choosing the new home shell, make sure that you're not
|
||||
choosing bash in disguise, as it is unclear if it avoids the security issue.
|
||||
|
||||
Another potential fix is to ensure that the user's home directory is not a
|
||||
shared mount and that they have no means of copying files outside of their
|
||||
restricted directories. This may require you to force the enabling of symlink
|
||||
munging on the server side.
|
||||
|
||||
A future version of openssh may have a change to the handling of forced
|
||||
commands that allows it to avoid using the user's home shell.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
The `~/.ssh/authorized_keys` file might have lines in it like this:
|
||||
|
||||
> ```
|
||||
> command="rrsync client/logs" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAzG...
|
||||
> command="rrsync -ro results" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAmk...
|
||||
> ```
|
||||
|
||||
## FILES
|
||||
|
||||
~/.ssh/authorized_keys
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
[**rsync**(1)](rsync.1), [**rsyncd.conf**(5)](rsyncd.conf.5)
|
||||
|
||||
## VERSION
|
||||
|
||||
This manpage is current for version @VERSION@ of rsync.
|
||||
|
||||
## CREDITS
|
||||
|
||||
rsync is distributed under the GNU General Public License. See the file
|
||||
[COPYING](COPYING) for details.
|
||||
|
||||
An rsync web site is available at <https://rsync.samba.org/> and its github
|
||||
project is <https://github.com/WayneD/rsync>.
|
||||
|
||||
## AUTHOR
|
||||
|
||||
The original rrsync perl script was written by Joe Smith. Many people have
|
||||
later contributed to it. The python version was created by Wayne Davison.
|
||||
|
||||
117
t_chmod_secure.c
Normal file
117
t_chmod_secure.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Test harness for do_chmod_at(). Confirms the symlink-TOCTOU
|
||||
* primitive used by CVE-2026-29518 (and its incomplete-fix follow-up
|
||||
* for chmod) is closed by do_chmod_at(): a parent directory component
|
||||
* being a symlink that escapes the receiver's confinement must be
|
||||
* rejected, while a parent symlink that resolves *within* the tree
|
||||
* must still work (so legitimate dir-symlinks are not regressed).
|
||||
*
|
||||
* Not linked into rsync itself.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
int dry_run = 0;
|
||||
int am_root = 0;
|
||||
int am_sender = 0;
|
||||
int read_only = 0;
|
||||
int list_only = 0;
|
||||
int copy_links = 0;
|
||||
int copy_unsafe_links = 0;
|
||||
extern int am_daemon, am_chrooted;
|
||||
|
||||
short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
|
||||
|
||||
static int errs = 0;
|
||||
|
||||
static void check(const char *label, int actual_rc, int expect_ok,
|
||||
const char *path, mode_t expected_mode)
|
||||
{
|
||||
struct stat st;
|
||||
int got_ok = (actual_rc == 0);
|
||||
if (got_ok != expect_ok) {
|
||||
fprintf(stderr, "FAIL [%s]: rc=%d errno=%d (%s), expected %s\n",
|
||||
label, actual_rc, errno, strerror(errno),
|
||||
expect_ok ? "success" : "rejection");
|
||||
errs++;
|
||||
return;
|
||||
}
|
||||
if (path && stat(path, &st) < 0) {
|
||||
fprintf(stderr, "FAIL [%s]: stat(%s) failed: %s\n",
|
||||
label, path, strerror(errno));
|
||||
errs++;
|
||||
return;
|
||||
}
|
||||
if (path && (st.st_mode & 07777) != expected_mode) {
|
||||
fprintf(stderr,
|
||||
"FAIL [%s]: %s mode is 0%o, expected 0%o\n",
|
||||
label, path, st.st_mode & 07777, expected_mode);
|
||||
errs++;
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "OK [%s]\n", label);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <module-dir>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
if (chdir(argv[1]) < 0) {
|
||||
perror("chdir");
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Simulate the daemon-without-chroot deployment that do_chmod_at()
|
||||
* defends. With am_daemon=0 or am_chrooted=1 the wrapper falls
|
||||
* through to plain do_chmod() and the symlink-race test would be
|
||||
* meaningless. */
|
||||
am_daemon = 1;
|
||||
am_chrooted = 0;
|
||||
|
||||
/* Test layout (all inside the directory we just chdir'd to):
|
||||
*
|
||||
* ./realdir/sentinel -- regular target file
|
||||
* ./inside_link -> realdir -- legitimate dir-symlink within the tree
|
||||
* ./escape_link -> ../trap -- attacker swap, target outside tree
|
||||
* ../trap/sentinel -- the file the attacker wants to alter
|
||||
*
|
||||
* The shell wrapper that calls this helper has set both sentinel
|
||||
* files to mode 0600 so we have a clean baseline to compare.
|
||||
*/
|
||||
|
||||
/* Scenario A: legitimate parent dir-symlink, chmod must succeed. */
|
||||
int rc = do_chmod_at("inside_link/sentinel", 0640);
|
||||
check("A: legit dir-symlink within tree",
|
||||
rc, 1, "realdir/sentinel", 0640);
|
||||
|
||||
/* Scenario B: parent symlink escapes the tree -- chmod must be
|
||||
* rejected and the outside file's mode must be unchanged. */
|
||||
rc = do_chmod_at("escape_link/sentinel", 0666);
|
||||
check("B: parent symlink escapes tree (the attack)",
|
||||
rc, 0, "../trap/sentinel", 0600);
|
||||
|
||||
/* Scenario C: plain relative path with no symlink components,
|
||||
* regression check that the safe wrapper doesn't break the
|
||||
* normal case. */
|
||||
rc = do_chmod_at("realdir/sentinel", 0644);
|
||||
check("C: plain relative path (regression check)",
|
||||
rc, 1, "realdir/sentinel", 0644);
|
||||
|
||||
/* Scenario D: top-level file, no parent directory component.
|
||||
* Falls back to do_chmod(); should succeed. */
|
||||
rc = do_chmod_at("topfile", 0640);
|
||||
check("D: top-level file, no parent component",
|
||||
rc, 1, "topfile", 0640);
|
||||
|
||||
if (errs)
|
||||
fprintf(stderr, "%d failure(s)\n", errs);
|
||||
return errs ? 1 : 0;
|
||||
}
|
||||
151
t_secure_relpath.c
Normal file
151
t_secure_relpath.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Test harness for secure_relative_open()'s front-door input
|
||||
* validation. Codex audit Finding 5 noted that the existing check
|
||||
*
|
||||
* if (strncmp(relpath, "../", 3) == 0 || strstr(relpath, "/../"))
|
||||
*
|
||||
* catches "../foo" and "foo/../bar" but misses bare ".." (an actual
|
||||
* one-level escape on platforms that fall back to the per-component
|
||||
* walk), as well as "a/..", "foo/..", and any other form that
|
||||
* decomposes to a ".." component when split on "/". The kernel-
|
||||
* enforced RESOLVE_BENEATH (Linux 5.6+) and O_RESOLVE_BENEATH
|
||||
* (FreeBSD 13+, macOS 15+) reject these in-kernel; the per-
|
||||
* component fallback used on NetBSD, OpenBSD, Solaris, Cygwin and
|
||||
* pre-5.6 Linux does not, so the validation must happen at the
|
||||
* front door.
|
||||
*
|
||||
* This helper invokes secure_relative_open() with each suspect
|
||||
* input and checks both the failure (rc < 0) and the errno
|
||||
* (EINVAL means "rejected at the front door"). Pre-fix, the kernel
|
||||
* may reject with a different errno (EXDEV from RESOLVE_BENEATH);
|
||||
* post-fix, the front-door check catches every variant up front
|
||||
* with a consistent EINVAL across platforms.
|
||||
*
|
||||
* Not linked into rsync itself.
|
||||
*/
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
int dry_run = 0;
|
||||
int am_root = 0;
|
||||
int am_sender = 0;
|
||||
int read_only = 0;
|
||||
int list_only = 0;
|
||||
int copy_links = 0;
|
||||
int copy_unsafe_links = 0;
|
||||
extern int am_daemon, am_chrooted;
|
||||
|
||||
short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
|
||||
|
||||
static int errs = 0;
|
||||
|
||||
static void check_relpath(const char *relpath)
|
||||
{
|
||||
int fd;
|
||||
int saved_errno;
|
||||
|
||||
errno = 0;
|
||||
fd = secure_relative_open(NULL, relpath, O_RDONLY | O_DIRECTORY, 0);
|
||||
saved_errno = errno;
|
||||
|
||||
if (fd >= 0) {
|
||||
fprintf(stderr,
|
||||
"FAIL [relpath=%-12s]: returned valid fd %d (escape) -- expected -1 EINVAL\n",
|
||||
relpath, fd);
|
||||
close(fd);
|
||||
errs++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (saved_errno != EINVAL) {
|
||||
fprintf(stderr,
|
||||
"FAIL [relpath=%-12s]: rejected but errno=%d (%s), expected EINVAL\n",
|
||||
relpath, saved_errno, strerror(saved_errno));
|
||||
errs++;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "OK [relpath=%-12s]: rejected with EINVAL\n", relpath);
|
||||
}
|
||||
|
||||
static void check_basedir(const char *basedir)
|
||||
{
|
||||
int fd;
|
||||
int saved_errno;
|
||||
|
||||
errno = 0;
|
||||
fd = secure_relative_open(basedir, "ok", O_RDONLY | O_DIRECTORY, 0);
|
||||
saved_errno = errno;
|
||||
|
||||
if (fd >= 0) {
|
||||
fprintf(stderr,
|
||||
"FAIL [basedir=%-12s]: returned valid fd %d -- expected -1 EINVAL\n",
|
||||
basedir, fd);
|
||||
close(fd);
|
||||
errs++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (saved_errno != EINVAL) {
|
||||
fprintf(stderr,
|
||||
"FAIL [basedir=%-12s]: rejected but errno=%d (%s), expected EINVAL\n",
|
||||
basedir, saved_errno, strerror(saved_errno));
|
||||
errs++;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "OK [basedir=%-12s]: rejected with EINVAL\n", basedir);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <test-dir>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
if (chdir(argv[1]) < 0) {
|
||||
perror("chdir");
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* secure_relative_open's daemon-only confinement protections only
|
||||
* fire when am_daemon && !am_chrooted (the threat model is the
|
||||
* daemon-no-chroot deployment), but the front-door input
|
||||
* validation runs unconditionally. We set am_daemon anyway so the
|
||||
* helper exercises the same code shape the receiver does. */
|
||||
am_daemon = 1;
|
||||
am_chrooted = 0;
|
||||
|
||||
mkdir("subdir", 0755);
|
||||
|
||||
/* Each of these relpaths must be rejected with EINVAL at the
|
||||
* secure_relative_open() front door. ".." is the actual one-level
|
||||
* escape; the others ("subdir/..", "subdir/../subdir") resolve
|
||||
* back to the start dir on systems that allow them, but we still
|
||||
* reject them as defence-in-depth: a path containing a ".." token
|
||||
* is suspicious and the caller should normalise before passing
|
||||
* it in. The "../foo" / "foo/../bar" / "/foo" / "/" cases are
|
||||
* regression checks for the existing checks. */
|
||||
check_relpath("..");
|
||||
check_relpath("../foo");
|
||||
check_relpath("subdir/..");
|
||||
check_relpath("subdir/../subdir");
|
||||
check_relpath("foo/../bar");
|
||||
check_relpath("/foo");
|
||||
check_relpath("/");
|
||||
|
||||
/* Same checks against basedir (which the codex Finding 2 fix
|
||||
* routes through the same RESOLVE_BENEATH-equivalent). Absolute
|
||||
* basedirs are operator-trusted and intentionally not validated
|
||||
* here. */
|
||||
check_basedir("..");
|
||||
check_basedir("../subdir");
|
||||
check_basedir("subdir/..");
|
||||
check_basedir("foo/../bar");
|
||||
|
||||
if (errs)
|
||||
fprintf(stderr, "\n%d failure(s)\n", errs);
|
||||
return errs ? 1 : 0;
|
||||
}
|
||||
5
t_stub.c
5
t_stub.c
@@ -3,7 +3,7 @@
|
||||
* functions, so that module test harnesses can run standalone.
|
||||
*
|
||||
* 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
|
||||
@@ -23,13 +23,14 @@
|
||||
|
||||
int do_fsync = 0;
|
||||
int inplace = 0;
|
||||
int am_daemon = 0;
|
||||
int am_chrooted = 0;
|
||||
int modify_window = 0;
|
||||
int preallocate_files = 0;
|
||||
int protect_args = 0;
|
||||
int module_id = -1;
|
||||
int relative_paths = 0;
|
||||
int module_dirlen = 0;
|
||||
int preserve_mtimes = 0;
|
||||
int preserve_xattrs = 0;
|
||||
int preserve_perms = 0;
|
||||
int preserve_executability = 0;
|
||||
|
||||
@@ -28,6 +28,9 @@ int am_root = 0;
|
||||
int am_sender = 1;
|
||||
int read_only = 0;
|
||||
int list_only = 0;
|
||||
int copy_links = 0;
|
||||
int copy_unsafe_links = 0;
|
||||
|
||||
short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
|
||||
|
||||
int
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_fail() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
# Test some foundational things.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
RSYNC_RSH="$scratchdir/src/support/lsh.sh"
|
||||
export RSYNC_RSH
|
||||
|
||||
echo $0 running
|
||||
|
||||
@@ -12,3 +14,48 @@ $RSYNC --version || test_fail '--version output failed'
|
||||
$RSYNC --info=help || test_fail '--info=help output failed'
|
||||
|
||||
$RSYNC --debug=help || test_fail '--debug=help output failed'
|
||||
|
||||
weird_name="A weird)name"
|
||||
|
||||
mkdir "$fromdir"
|
||||
mkdir "$fromdir/$weird_name"
|
||||
|
||||
append_line() {
|
||||
echo "$1"
|
||||
echo "$1" >>"$fromdir/$weird_name/file"
|
||||
}
|
||||
|
||||
append_line test1
|
||||
checkit "$RSYNC -ai '$fromdir/' '$todir/'" "$fromdir" "$todir"
|
||||
|
||||
copy_weird() {
|
||||
checkit "$RSYNC $1 --rsync-path='$RSYNC' '$2$fromdir/$weird_name/' '$3$todir/$weird_name'" "$fromdir" "$todir"
|
||||
}
|
||||
|
||||
append_line test2
|
||||
copy_weird '-ai' 'lh:' ''
|
||||
|
||||
append_line test3
|
||||
copy_weird '-ai' '' 'lh:'
|
||||
|
||||
append_line test4
|
||||
copy_weird '-ais' 'lh:' ''
|
||||
|
||||
append_line test5
|
||||
copy_weird '-ais' '' 'lh:'
|
||||
|
||||
echo test6
|
||||
|
||||
touch "$fromdir/one" "$fromdir/two"
|
||||
(cd "$fromdir" && $RSYNC -ai --old-args --rsync-path="$RSYNC" lh:'one two' "$todir/")
|
||||
if [ ! -f "$todir/one" ] || [ ! -f "$todir/two" ]; then
|
||||
test_fail "old-args copy of 'one two' failed"
|
||||
fi
|
||||
|
||||
echo test7
|
||||
|
||||
rm "$todir/one" "$todir/two"
|
||||
(cd "$fromdir" && RSYNC_OLD_ARGS=1 $RSYNC -ai --rsync-path="$RSYNC" lh:'one two' "$todir/")
|
||||
|
||||
# The script would have aborted on error, so getting here means we've won.
|
||||
exit 0
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
. $suitedir/rsync.fns
|
||||
|
||||
$RSYNC --version | grep "[, ] ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support"
|
||||
$RSYNC -VV | grep '"ACLs": true' >/dev/null || test_skipped "Rsync is configured without ACL support"
|
||||
|
||||
case "$setfacl_nodef" in
|
||||
true) test_skipped "I don't know how to use your setfacl command" ;;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
. $suitedir/rsync.fns
|
||||
|
||||
$RSYNC --version | grep "[, ] ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support"
|
||||
$RSYNC -VV | grep '"ACLs": true' >/dev/null || test_skipped "Rsync is configured without ACL support"
|
||||
|
||||
makepath "$fromdir/foo"
|
||||
echo something >"$fromdir/file1"
|
||||
|
||||
113
testsuite/alt-dest-symlink-race.test
Executable file
113
testsuite/alt-dest-symlink-race.test
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 by Andrew Tridgell
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Regression test for the basedir-confinement gap in
|
||||
# secure_relative_open(). The function opens basedir with a plain
|
||||
# openat(AT_FDCWD, basedir, O_RDONLY | O_DIRECTORY), without
|
||||
# RESOLVE_BENEATH or a per-component O_NOFOLLOW walk, so a parent
|
||||
# symlink ON basedir is followed unrestrictedly. RESOLVE_BENEATH is
|
||||
# then applied only to relpath, anchored at the wrong directory.
|
||||
#
|
||||
# The receiver's basis-file lookup at receiver.c passes
|
||||
# basis_dir[fnamecmp_type] (from --copy-dest / --link-dest /
|
||||
# --compare-dest -- all sender-controllable in daemon mode) as
|
||||
# basedir. A daemon-module attacker with write access can plant a
|
||||
# symlink at module/cd -> /outside, then run --link-dest=cd to
|
||||
# make the daemon's basis-file lookup resolve into /outside,
|
||||
# leaking the contents of daemon-readable files via the rsync
|
||||
# delta-rolling read-disclosure primitive.
|
||||
#
|
||||
# We detect the escape by leveraging --link-dest: when basis
|
||||
# matches source exactly (content + mtime + mode), --link-dest
|
||||
# hard-links the destination to the basis file. With the bug, the
|
||||
# destination ends up as a hard link to the outside-the-module
|
||||
# file (same inode). With the fix, no basis is found and the
|
||||
# destination is a fresh copy (different inode).
|
||||
#
|
||||
# The vulnerable code path is the same on every platform
|
||||
# (including the per-component fallback on systems without
|
||||
# RESOLVE_BENEATH), so this test is not platform-gated.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
mod="$scratchdir/module"
|
||||
outside="$scratchdir/outside"
|
||||
src="$scratchdir/src"
|
||||
conf="$scratchdir/test-rsyncd.conf"
|
||||
|
||||
rm -rf "$mod" "$outside" "$src"
|
||||
mkdir -p "$mod" "$outside" "$src"
|
||||
|
||||
# Portable inode-number helper (GNU coreutils stat -c, BSD stat -f).
|
||||
file_inode() {
|
||||
stat -c %i "$1" 2>/dev/null || stat -f %i "$1"
|
||||
}
|
||||
|
||||
# Outside-the-module file an attacker would like the daemon to
|
||||
# treat as a basis.
|
||||
echo "OUTSIDE_SECRET_DATA" > "$outside/target.txt"
|
||||
chmod 0644 "$outside/target.txt"
|
||||
|
||||
# The symlink trap planted in the module by the local attacker.
|
||||
ln -s "$outside" "$mod/cd"
|
||||
|
||||
# Source file matches outside/target.txt exactly (content + mtime
|
||||
# + mode) so --link-dest will hard-link the destination to the
|
||||
# basis file iff the daemon's basedir lookup reaches outside/.
|
||||
echo "OUTSIDE_SECRET_DATA" > "$src/target.txt"
|
||||
touch -r "$outside/target.txt" "$src/target.txt"
|
||||
chmod 0644 "$src/target.txt"
|
||||
|
||||
# When running as root the daemon would drop to "nobody" by
|
||||
# default, which can't write into the test scratch dir. Force the
|
||||
# daemon to keep our uid/gid in that case so the basis-link
|
||||
# transfer can actually create the destination file. (Non-root
|
||||
# can't specify uid/gid in rsyncd.conf -- comment them out then.)
|
||||
my_uid=`get_testuid`
|
||||
root_uid=`get_rootuid`
|
||||
root_gid=`get_rootgid`
|
||||
uid_setting="uid = $root_uid"
|
||||
gid_setting="gid = $root_gid"
|
||||
if test x"$my_uid" != x"$root_uid"; then
|
||||
uid_setting="#$uid_setting"
|
||||
gid_setting="#$gid_setting"
|
||||
fi
|
||||
|
||||
cat > "$conf" <<EOF
|
||||
use chroot = no
|
||||
$uid_setting
|
||||
$gid_setting
|
||||
log file = $scratchdir/rsyncd.log
|
||||
[upload]
|
||||
path = $mod
|
||||
use chroot = no
|
||||
read only = no
|
||||
EOF
|
||||
|
||||
# Recursive --link-dest push directly into the module root. We
|
||||
# avoid pushing into a destination subdir because the receiver
|
||||
# would chdir into it before resolving --link-dest, making the
|
||||
# relative basedir "cd" resolve in the wrong CWD and masking the
|
||||
# bug. The realistic attack pushes into the module root (or the
|
||||
# attacker uses a basedir path that resolves correctly from
|
||||
# whichever subdir the receiver chdirs into).
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC -rtp --link-dest=cd "$src/" rsync://localhost/upload/ \
|
||||
>/dev/null 2>&1 || true
|
||||
|
||||
if [ ! -f "$mod/target.txt" ]; then
|
||||
test_fail "destination file was not created -- daemon transfer failed before the test could observe the basedir behaviour"
|
||||
fi
|
||||
|
||||
outside_inode=$(file_inode "$outside/target.txt")
|
||||
dst_inode=$(file_inode "$mod/target.txt")
|
||||
|
||||
if [ "$outside_inode" = "$dst_inode" ]; then
|
||||
test_fail "basedir-escape: --link-dest hard-linked module/target.txt to outside/target.txt (inode $outside_inode); daemon's basis-file lookup followed the parent symlink on the basedir"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
68
testsuite/alt-dest.test
Normal file
68
testsuite/alt-dest.test
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2004-2022 Wayne Davison
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Test rsync handling of --compare-dest and similar options.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
alt1dir="$tmpdir/alt1"
|
||||
alt2dir="$tmpdir/alt2"
|
||||
alt3dir="$tmpdir/alt3"
|
||||
|
||||
SSH="$scratchdir/src/support/lsh.sh"
|
||||
|
||||
# Build some files/dirs/links to copy
|
||||
|
||||
hands_setup
|
||||
|
||||
# Setup the alt and chk dirs
|
||||
$RSYNC -av --include=text --include='*/' --exclude='*' "$fromdir/" "$alt1dir/"
|
||||
$RSYNC -av --include=etc-ltr-list --include='*/' --exclude='*' "$fromdir/" "$alt2dir/"
|
||||
|
||||
# Create a side dir where there is a candidate destfile of the same name as a sourcefile
|
||||
echo "This is a test file" >"$fromdir/likely"
|
||||
|
||||
mkdir "$alt3dir"
|
||||
echo "This is a test file" >"$alt3dir/likely"
|
||||
|
||||
sleep 1
|
||||
touch "$fromdir/dir/text" "$fromdir/likely"
|
||||
|
||||
$RSYNC -av --exclude=/text --exclude=etc-ltr-list "$fromdir/" "$chkdir/"
|
||||
|
||||
# Let's do it!
|
||||
checkit "$RSYNC -avv --no-whole-file \
|
||||
--compare-dest='$alt1dir' --compare-dest='$alt2dir' \
|
||||
'$fromdir/' '$todir/'" "$chkdir" "$todir"
|
||||
|
||||
rm -rf "$todir"
|
||||
checkit "$RSYNC -avv --no-whole-file \
|
||||
--copy-dest='$alt1dir' --copy-dest='$alt2dir' \
|
||||
'$fromdir/' '$todir/'" "$fromdir" "$todir"
|
||||
|
||||
# Test that copy_file() works correctly with tmpfiles
|
||||
for maybe_inplace in '' --inplace; do
|
||||
rm -rf "$todir"
|
||||
checkit "$RSYNC -av $maybe_inplace --copy-dest='$alt3dir' \
|
||||
'$fromdir/' '$todir/'" "$fromdir" "$todir"
|
||||
|
||||
for srchost in '' 'localhost:'; do
|
||||
if [ -z "$srchost" ]; then
|
||||
desthost='localhost:'
|
||||
else
|
||||
desthost=''
|
||||
fi
|
||||
|
||||
rm -rf "$todir"
|
||||
checkit "$RSYNC -ave '$SSH' --rsync-path='$RSYNC' $maybe_inplace \
|
||||
--copy-dest='$alt3dir' '$srchost$fromdir/' '$desthost$todir/'" \
|
||||
"$fromdir" "$todir"
|
||||
done
|
||||
done
|
||||
|
||||
# The script would have aborted on error, so getting here means we've won.
|
||||
exit 0
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
$RSYNC --version | grep "[, ] atimes" >/dev/null || test_skipped "Rsync is configured without atimes support"
|
||||
$RSYNC -VV | grep '"atimes": true' >/dev/null || test_skipped "Rsync is configured without atimes support"
|
||||
|
||||
mkdir "$fromdir"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2004-2021 Wayne Davison
|
||||
# Copyright (C) 2004-2022 Wayne Davison
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
206
testsuite/bare-do-open-symlink-race.test
Executable file
206
testsuite/bare-do-open-symlink-race.test
Executable file
@@ -0,0 +1,206 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 by Andrew Tridgell
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Regression test for codex audit Findings 3b and 3c:
|
||||
#
|
||||
# 3b: generator.c:1905 -- the in-place backup creation opens
|
||||
# backupptr via bare do_open(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL).
|
||||
# With --backup-dir set to an attacker-planted parent symlink,
|
||||
# the backup file is written outside the module under the
|
||||
# daemon's authority.
|
||||
#
|
||||
# 3c-symlink: syscall.c:207 -- do_symlink_at falls through to bare
|
||||
# do_symlink for am_root < 0 (fake-super), which then opens
|
||||
# the destination path with bare open() (final-component
|
||||
# fake-super file). A parent symlink on the destination path
|
||||
# redirects the file creation outside the module.
|
||||
#
|
||||
# 3c-mknod: syscall.c:506 -- do_mknod_at falls through to bare
|
||||
# do_mknod for am_root < 0, same path-based open(). For
|
||||
# FIFOs/sockets/devices the bare path is also used.
|
||||
#
|
||||
# Each scenario plants a "secret" file outside the module at a
|
||||
# location the symlink trap points to. The check is that the
|
||||
# outside file's content and mode are unchanged after the attack
|
||||
# attempt.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
# All three scenarios depend on receiver-side daemon code paths
|
||||
# that are only secured on platforms with a working
|
||||
# secure_relative_open. The chdir/chmod tests already skip the
|
||||
# same set; mirror that.
|
||||
case "$(uname -s)" in
|
||||
SunOS|OpenBSD|NetBSD|CYGWIN*)
|
||||
test_skipped "secure_relative_open relies on RESOLVE_BENEATH-equivalent kernel support not available on $(uname -s)"
|
||||
;;
|
||||
esac
|
||||
|
||||
mod="$scratchdir/module"
|
||||
outside="$scratchdir/outside"
|
||||
src="$scratchdir/src"
|
||||
conf="$scratchdir/test-rsyncd.conf"
|
||||
|
||||
# Portable inode-and-mode helpers.
|
||||
file_mode() {
|
||||
stat -c %a "$1" 2>/dev/null || stat -f %Lp "$1"
|
||||
}
|
||||
|
||||
setup() {
|
||||
rm -rf "$mod" "$outside" "$src"
|
||||
mkdir -p "$mod" "$outside" "$src"
|
||||
|
||||
echo "OUTSIDE_PROTECTED_DATA" > "$outside/target.txt"
|
||||
chmod 0644 "$outside/target.txt"
|
||||
outside_pristine="$scratchdir/outside-pristine.txt"
|
||||
cp -p "$outside/target.txt" "$outside_pristine"
|
||||
|
||||
ln -s "$outside" "$mod/cd"
|
||||
}
|
||||
|
||||
verify_outside_unchanged() {
|
||||
label="$1"
|
||||
mode=$(file_mode "$outside/target.txt")
|
||||
case "$mode" in
|
||||
644|0644) ;;
|
||||
*) test_fail "$label: outside/target.txt mode changed from 644 to $mode" ;;
|
||||
esac
|
||||
if ! cmp -s "$outside/target.txt" "$outside_pristine"; then
|
||||
test_fail "$label: outside/target.txt content changed -- daemon followed the cd symlink"
|
||||
fi
|
||||
}
|
||||
|
||||
verify_outside_unchanged_or_absent() {
|
||||
label="$1"
|
||||
target="$2" # specific file under outside/ to check absence of
|
||||
if [ -e "$outside/$target" ]; then
|
||||
test_fail "$label: outside/$target was created -- daemon followed the cd symlink"
|
||||
fi
|
||||
}
|
||||
|
||||
# When running as root the daemon would drop to "nobody" by default
|
||||
# and fail to write into the test scratch dir. Force it to keep our
|
||||
# uid/gid in that case so the receiver actually runs the code paths
|
||||
# we want to test.
|
||||
my_uid=`get_testuid`
|
||||
root_uid=`get_rootuid`
|
||||
root_gid=`get_rootgid`
|
||||
uid_setting="uid = $root_uid"
|
||||
gid_setting="gid = $root_gid"
|
||||
if test x"$my_uid" != x"$root_uid"; then
|
||||
uid_setting="#$uid_setting"
|
||||
gid_setting="#$gid_setting"
|
||||
fi
|
||||
|
||||
|
||||
############################################################
|
||||
# Scenario 3b: --inplace --backup --backup-dir=cd
|
||||
#
|
||||
# Pre-create module/target.txt so the receiver enters the in-place
|
||||
# update path; a backup of the existing content must be made
|
||||
# before the update. With --backup-dir=cd, backupptr resolves to
|
||||
# "cd/target.txt"; with the bug, robust_unlink and the bare
|
||||
# do_open at generator.c:1905 both follow the cd symlink, the
|
||||
# unlink deletes outside/target.txt and the create writes the
|
||||
# pre-existing module/target.txt content there.
|
||||
############################################################
|
||||
|
||||
setup
|
||||
echo "EXISTING_MODULE_DATA" > "$mod/target.txt"
|
||||
chmod 0666 "$mod/target.txt"
|
||||
echo "NEW_DATA_FROM_SENDER" > "$src/target.txt"
|
||||
chmod 0644 "$src/target.txt"
|
||||
|
||||
cat > "$conf" <<EOF
|
||||
use chroot = no
|
||||
$uid_setting
|
||||
$gid_setting
|
||||
log file = $scratchdir/rsyncd.log
|
||||
[upload]
|
||||
path = $mod
|
||||
use chroot = no
|
||||
read only = no
|
||||
EOF
|
||||
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC --inplace --backup --backup-dir=cd "$src/target.txt" \
|
||||
rsync://localhost/upload/target.txt >/dev/null 2>&1 || true
|
||||
|
||||
verify_outside_unchanged "3b inplace+backup-dir=cd"
|
||||
|
||||
|
||||
############################################################
|
||||
# Scenario 3c-symlink: fake-super symlink push to a path with a
|
||||
# symlinked parent
|
||||
#
|
||||
# With "fake super = yes" set on the module, the receiver
|
||||
# represents symlinks as fake-super files (regular files with the
|
||||
# link target written to them). The path-based open() in
|
||||
# do_symlink's fake-super branch follows parent symlinks. We push
|
||||
# a single symlink to the destination path "cd/sym" so the
|
||||
# receiver's create-file call lands at "cd/sym" relative to the
|
||||
# module root, where cd is the symlink trap.
|
||||
############################################################
|
||||
|
||||
setup
|
||||
|
||||
mkdir -p "$src/cd"
|
||||
ln -s /etc/passwd "$src/cd/sym"
|
||||
|
||||
cat > "$conf" <<EOF
|
||||
use chroot = no
|
||||
$uid_setting
|
||||
$gid_setting
|
||||
log file = $scratchdir/rsyncd.log
|
||||
[upload_fake]
|
||||
path = $mod
|
||||
use chroot = no
|
||||
read only = no
|
||||
fake super = yes
|
||||
EOF
|
||||
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC -rl "$src/" rsync://localhost/upload_fake/ >/dev/null 2>&1 || true
|
||||
|
||||
verify_outside_unchanged_or_absent "3c-symlink fake-super symlink push" "sym"
|
||||
|
||||
|
||||
############################################################
|
||||
# Scenario 3c-mknod: fake-super FIFO push to a path with a
|
||||
# symlinked parent
|
||||
#
|
||||
# Similar to 3c-symlink but for special files. mkfifo works
|
||||
# without root; we push a FIFO and verify the receiver doesn't
|
||||
# create a fake-super file at outside/fifo.
|
||||
############################################################
|
||||
|
||||
setup
|
||||
|
||||
mkdir -p "$src/cd"
|
||||
mkfifo "$src/cd/fifo" 2>/dev/null
|
||||
if [ ! -p "$src/cd/fifo" ]; then
|
||||
test_skipped "mkfifo unavailable; cannot exercise 3c-mknod"
|
||||
fi
|
||||
|
||||
cat > "$conf" <<EOF
|
||||
use chroot = no
|
||||
$uid_setting
|
||||
$gid_setting
|
||||
log file = $scratchdir/rsyncd.log
|
||||
[upload_fake]
|
||||
path = $mod
|
||||
use chroot = no
|
||||
read only = no
|
||||
fake super = yes
|
||||
EOF
|
||||
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC -rD "$src/" rsync://localhost/upload_fake/ >/dev/null 2>&1 || true
|
||||
|
||||
verify_outside_unchanged_or_absent "3c-mknod fake-super FIFO push" "fifo"
|
||||
|
||||
exit 0
|
||||
135
testsuite/chdir-symlink-race.test
Executable file
135
testsuite/chdir-symlink-race.test
Executable file
@@ -0,0 +1,135 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 by Andrew Tridgell
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Regression test for the symlink-TOCTOU class of bug at the receiver's
|
||||
# chdir(). After the CVE-2026-29518 fix to secure_relative_open(), an
|
||||
# attack remained where the receiver's chdir() into a destination
|
||||
# subdirectory followed an attacker-planted symlink, escaping the
|
||||
# module. Every subsequent path-relative syscall (open, chmod, lchown,
|
||||
# utimes, etc.) inherited the escape -- secure_relative_open's
|
||||
# RESOLVE_BENEATH anchor itself was outside the module by then, so it
|
||||
# stopped protecting against anything.
|
||||
#
|
||||
# This test runs an actual rsync daemon (via RSYNC_CONNECT_PROG to
|
||||
# avoid the network) configured with "use chroot = no", plants a
|
||||
# symlink at module/subdir -> ../outside, and runs four flavours of
|
||||
# rsync transfer that previously all reached files in ../outside:
|
||||
#
|
||||
# 1. single-file dest = subdir/target.txt (the original poc_chmod)
|
||||
# 2. -r src/subdir/ to upload/subdir/ (the chdir-escape case)
|
||||
# 3. -r src/subdir/ to upload/subdir/ (no --size-only: forces basis read+write)
|
||||
# 4. -r src/ to upload/ (was already protected by the
|
||||
# original CVE-2026-29518 fix;
|
||||
# regression-checked here)
|
||||
#
|
||||
# All four must leave the outside-the-module sentinel file's mode AND
|
||||
# content unchanged.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
case "$(uname -s)" in
|
||||
SunOS|OpenBSD|NetBSD|CYGWIN*)
|
||||
test_skipped "secure chdir relies on RESOLVE_BENEATH-equivalent kernel support not available on $(uname -s)"
|
||||
;;
|
||||
esac
|
||||
|
||||
mod="$scratchdir/module"
|
||||
outside="$scratchdir/outside"
|
||||
src="$scratchdir/src"
|
||||
conf="$scratchdir/test-rsyncd.conf"
|
||||
|
||||
rm -rf "$mod" "$outside" "$src"
|
||||
mkdir -p "$mod" "$outside" "$src" "$src/subdir"
|
||||
|
||||
# Portable octal-mode helper -- macOS and FreeBSD's stat use -f, GNU
|
||||
# coreutils stat uses -c.
|
||||
file_mode() {
|
||||
stat -c %a "$1" 2>/dev/null || stat -f %Lp "$1"
|
||||
}
|
||||
|
||||
# The "secret" file outside the module the attacker is trying to alter.
|
||||
# Save a pristine copy alongside it so we can compare with cmp(1) rather
|
||||
# than depending on sha1sum/shasum/sha1, which differ across platforms.
|
||||
echo "OUTSIDE_SECRET_DATA" > "$outside/target.txt"
|
||||
chmod 0600 "$outside/target.txt"
|
||||
outside_pristine="$scratchdir/outside-pristine.txt"
|
||||
cp -p "$outside/target.txt" "$outside_pristine"
|
||||
|
||||
# Symlink trap planted in the module by the local attacker.
|
||||
ln -s "$outside" "$mod/subdir"
|
||||
|
||||
# Source files the sender will push: same size as the outside target,
|
||||
# different content, mode 0666 (the perms the attacker tries to push).
|
||||
SIZE=$(stat -c %s "$outside/target.txt" 2>/dev/null \
|
||||
|| stat -f %z "$outside/target.txt")
|
||||
head -c "$SIZE" /dev/urandom > "$src/target.txt"
|
||||
head -c "$SIZE" /dev/urandom > "$src/subdir/target.txt"
|
||||
chmod 0666 "$src/target.txt" "$src/subdir/target.txt"
|
||||
|
||||
cat > "$conf" <<EOF
|
||||
use chroot = no
|
||||
log file = $scratchdir/rsyncd.log
|
||||
[upload]
|
||||
path = $mod
|
||||
use chroot = no
|
||||
read only = no
|
||||
EOF
|
||||
|
||||
reset_outside() {
|
||||
chmod 0600 "$outside/target.txt"
|
||||
echo "OUTSIDE_SECRET_DATA" > "$outside/target.txt"
|
||||
}
|
||||
|
||||
verify_unchanged() {
|
||||
label="$1"
|
||||
mode=$(file_mode "$outside/target.txt")
|
||||
case "$mode" in
|
||||
600|0600) ;;
|
||||
*) test_fail "$label: outside file mode changed from 600 to $mode (chmod escape)" ;;
|
||||
esac
|
||||
if ! cmp -s "$outside/target.txt" "$outside_pristine"; then
|
||||
test_fail "$label: outside file content changed (write escape)"
|
||||
fi
|
||||
}
|
||||
|
||||
run_attack() {
|
||||
label="$1"; shift
|
||||
reset_outside
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC "$@" >/dev/null 2>&1 || true
|
||||
verify_unchanged "$label"
|
||||
}
|
||||
|
||||
# 1. The original poc_chmod scenario: single file, dest path with
|
||||
# the symlinked subdir as a path component. With --size-only the
|
||||
# receiver normally skips the basis open and goes straight to chmod
|
||||
# -- only the chdir-escape blocks the chmod from reaching outside.
|
||||
run_attack "single-file --size-only" \
|
||||
-tp --size-only \
|
||||
"$src/target.txt" rsync://localhost/upload/subdir/target.txt
|
||||
|
||||
# 2. -r push into the symlinked subdir: receiver chdir's into "subdir",
|
||||
# follows the symlink, ends up in outside.
|
||||
run_attack "-r --size-only into subdir/" \
|
||||
-rtp --size-only \
|
||||
"$src/subdir/" rsync://localhost/upload/subdir/
|
||||
|
||||
# 3. Same but no --size-only -- forces the basis-file open and a real
|
||||
# rename, so this exercises the read-disclosure and write-escape
|
||||
# paths together.
|
||||
run_attack "-r without --size-only into subdir/" \
|
||||
-rtp \
|
||||
"$src/subdir/" rsync://localhost/upload/subdir/
|
||||
|
||||
# 4. -r src/ to upload/ -- this case was already covered by the
|
||||
# original CVE-2026-29518 fix because the receiver stays at module
|
||||
# root and operates on slashed paths. Regression check.
|
||||
run_attack "-r --size-only into upload/ root" \
|
||||
-rtp --size-only \
|
||||
"$src/" rsync://localhost/upload/
|
||||
|
||||
exit 0
|
||||
68
testsuite/chmod-symlink-race.test
Executable file
68
testsuite/chmod-symlink-race.test
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 by Andrew Tridgell
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Regression test for the symlink-TOCTOU class of bug applied to
|
||||
# chmod() on the receiver side. The CVE-2026-29518 fix used
|
||||
# secure_relative_open() for the basis-file open, but every other
|
||||
# path-based syscall the receiver runs on sender-controllable paths
|
||||
# is vulnerable to the same primitive: a local attacker swaps a
|
||||
# symlink into one of the parent directory components between the
|
||||
# receiver's check and its act, and the syscall escapes the module.
|
||||
#
|
||||
# This test exercises the new do_chmod_at() wrapper via the
|
||||
# t_chmod_secure helper. The helper sets up two scenarios:
|
||||
# - a parent dir-symlink that resolves WITHIN the module tree
|
||||
# (legitimate -K-style use, must continue to work)
|
||||
# - a parent dir-symlink that escapes the module tree (the
|
||||
# attack, must be rejected)
|
||||
# plus two regression scenarios (plain relative path, top-level
|
||||
# file) that just confirm the safe wrapper doesn't break the
|
||||
# normal case.
|
||||
#
|
||||
# The kernel-enforced "stay below dirfd" path resolution is
|
||||
# only available on Linux 5.6+, FreeBSD 13+, and macOS 15+.
|
||||
# Skip on platforms that fall back to per-component O_NOFOLLOW
|
||||
# (Solaris, OpenBSD, NetBSD, Cygwin); the per-component fallback
|
||||
# would also reject the attack but the legitimate dir-symlink
|
||||
# scenario would fail there.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
case "$(uname -s)" in
|
||||
SunOS|OpenBSD|NetBSD|CYGWIN*)
|
||||
test_skipped "do_chmod_at relies on RESOLVE_BENEATH-equivalent kernel support not available on $(uname -s)"
|
||||
;;
|
||||
esac
|
||||
|
||||
mod="$scratchdir/module"
|
||||
trap_outside="$scratchdir/trap"
|
||||
rm -rf "$mod" "$trap_outside"
|
||||
mkdir -p "$mod/realdir" "$trap_outside"
|
||||
|
||||
# Set up the four file-system objects the helper expects:
|
||||
echo bystander > "$mod/realdir/sentinel"
|
||||
chmod 0600 "$mod/realdir/sentinel"
|
||||
echo target > "$trap_outside/sentinel"
|
||||
chmod 0600 "$trap_outside/sentinel"
|
||||
ln -s realdir "$mod/inside_link"
|
||||
ln -s ../trap "$mod/escape_link"
|
||||
echo top > "$mod/topfile"
|
||||
chmod 0600 "$mod/topfile"
|
||||
|
||||
"$TOOLDIR/t_chmod_secure" "$mod" || \
|
||||
test_fail "t_chmod_secure reported failures (see stderr above)"
|
||||
|
||||
# Sanity-check from the shell side too: the outside file's mode must
|
||||
# still be 0600 -- the helper checked this, but a second look from
|
||||
# the shell guards against a helper-internal stat() bug.
|
||||
mode=$(stat -c '%a' "$trap_outside/sentinel" 2>/dev/null \
|
||||
|| stat -f '%Lp' "$trap_outside/sentinel" 2>/dev/null)
|
||||
if [ "$mode" != "600" ]; then
|
||||
test_fail "outside sentinel mode changed from 600 to $mode -- chmod escaped the module"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2004-2021 Wayne Davison
|
||||
# Copyright (C) 2004-2022 Wayne Davison
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2004-2021 Wayne Davison
|
||||
# Copyright (C) 2004-2022 Wayne Davison
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
case $0 in
|
||||
*fake*)
|
||||
$RSYNC --version | grep "[, ] xattrs" >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
|
||||
$RSYNC -VV | grep '"xattrs": true' >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
|
||||
RSYNC="$RSYNC --fake-super"
|
||||
TLS_ARGS="$TLS_ARGS --fake-super"
|
||||
case "$HOST_OS" in
|
||||
|
||||
80
testsuite/clean-fname-underflow.test
Normal file
80
testsuite/clean-fname-underflow.test
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
# clean-fname-underflow.test
|
||||
# Ensure clean_fname() does not read-before-buffer when collapsing "..".
|
||||
# This exercises the --server path where a crafted merge filename hits clean_fname().
|
||||
#
|
||||
# Usage:
|
||||
# ./configure && make
|
||||
# make check TESTS='clean-fname-underflow.test'
|
||||
|
||||
set -eu
|
||||
|
||||
# clean_fname() is platform-agnostic; the test only needs to run on one
|
||||
# host. Skip on non-Linux to avoid quirks in older /bin/sh implementations
|
||||
# (Solaris exited silently here, producing an empty log under set -eu).
|
||||
# runtests.sh expects $scratchdir/whyskipped to exist when a test exits 77.
|
||||
case "$(uname -s)" in
|
||||
Linux) ;;
|
||||
*)
|
||||
if [ -n "${scratchdir:-}" ]; then
|
||||
echo "Linux-only test (uname -s = $(uname -s))" > "$scratchdir/whyskipped"
|
||||
fi
|
||||
exit 77 ;;
|
||||
esac
|
||||
|
||||
# Try to find the just-built rsync binary if RSYNC_BIN isn't set.
|
||||
if [ -z "${RSYNC_BIN:-}" ]; then
|
||||
if [ -x "./rsync" ]; then
|
||||
RSYNC_BIN=$(pwd)/rsync
|
||||
elif [ -x "../rsync" ]; then
|
||||
RSYNC_BIN=$(cd .. && pwd)/rsync
|
||||
else
|
||||
RSYNC_BIN=rsync
|
||||
fi
|
||||
fi
|
||||
|
||||
workdir="${TMPDIR:-/tmp}/rsync-clean-fname.$$"
|
||||
mkdir -p "$workdir"
|
||||
trap 'rm -rf "$workdir"' EXIT INT TERM
|
||||
cd "$workdir"
|
||||
|
||||
# Minimal rsyncd.conf using chroot so the crafted path reaches the server parser.
|
||||
cat > rsyncd.conf <<'EOF'
|
||||
pid file = rsyncd.pid
|
||||
use chroot = true
|
||||
[mod]
|
||||
path = ./mod
|
||||
read only = false
|
||||
EOF
|
||||
mkdir -p mod
|
||||
|
||||
# Start daemon on a random high port.
|
||||
PORT=$(awk 'BEGIN{srand(); printf "%d", 20000+int(rand()*20000)}')
|
||||
"$RSYNC_BIN" --daemon --no-detach --config=rsyncd.conf --port="$PORT" >/dev/null 2>&1 &
|
||||
DAEMON_PID=$!
|
||||
# Give the daemon a moment to come up.
|
||||
# Use integer second; subsecond sleep is not portable (e.g. Solaris /usr/bin/sleep).
|
||||
sleep 1
|
||||
|
||||
# Invoke the server-side path. We don't need a real transfer; we just want to
|
||||
# ensure clean_fname() doesn't crash when given "a/../test" via --filter=merge.
|
||||
EXIT_OK=0
|
||||
if "$RSYNC_BIN" --server --sender -vlr --filter='merge a/../test' . mod/ >/dev/null 2>&1; then
|
||||
EXIT_OK=1
|
||||
else
|
||||
status=$?
|
||||
# Non-zero exit is expected for bogus input; ensure it wasn't a signal/crash.
|
||||
if [ $status -lt 128 ]; then
|
||||
EXIT_OK=1
|
||||
fi
|
||||
fi
|
||||
|
||||
kill "$DAEMON_PID" >/dev/null 2>&1 || true
|
||||
|
||||
if [ "$EXIT_OK" -ne 1 ]; then
|
||||
echo "clean-fname-underflow.test: rsync exited due to a signal or unexpected status"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "OK: clean_fname() handled 'a/../test' without crashing"
|
||||
exit 0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user