mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-06-06 13:16:14 -04:00
Compare commits
26 Commits
v3.2.7-sec
...
v3.3.0pre1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a06b2edb0 | ||
|
|
273dced284 | ||
|
|
b6e2321973 | ||
|
|
fe95a9369a | ||
|
|
6ae7f4085a | ||
|
|
0f599d3641 | ||
|
|
c3d3b49d72 | ||
|
|
c69dc7a5ab | ||
|
|
2c82006b1f | ||
|
|
0698ea9aeb | ||
|
|
90df93e446 | ||
|
|
5c93dedf45 | ||
|
|
f1e3434b59 | ||
|
|
48252c3c2b | ||
|
|
5b67ff2a86 | ||
|
|
8990ad96de | ||
|
|
0f44e864d4 | ||
|
|
ab0d5021ed | ||
|
|
7402896523 | ||
|
|
5374994089 | ||
|
|
526366129a | ||
|
|
556a2c5bc2 | ||
|
|
27feda0436 | ||
|
|
bf96cd314c | ||
|
|
1b2688807d | ||
|
|
08ec80ac65 |
76
.github/workflows/almalinux-8-build.yml
vendored
76
.github/workflows/almalinux-8-build.yml
vendored
@@ -1,76 +0,0 @@
|
||||
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
|
||||
125
.github/workflows/build.yml
vendored
Normal file
125
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
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@v3
|
||||
- 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@v3
|
||||
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@v3
|
||||
- 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@v3
|
||||
with:
|
||||
name: macos-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
|
||||
cygwin-build:
|
||||
runs-on: windows-2022
|
||||
if: (github.event_name == 'schedule' || contains(github.event.head_commit.message, '[buildall]'))
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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
|
||||
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@v3
|
||||
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
55
.github/workflows/cygwin-build.yml
vendored
@@ -1,55 +0,0 @@
|
||||
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
49
.github/workflows/freebsd-build.yml
vendored
@@ -1,49 +0,0 @@
|
||||
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
57
.github/workflows/macos-build.yml
vendored
@@ -1,57 +0,0 @@
|
||||
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
51
.github/workflows/netbsd-build.yml
vendored
@@ -1,51 +0,0 @@
|
||||
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
52
.github/workflows/openbsd-build.yml
vendored
@@ -1,52 +0,0 @@
|
||||
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
49
.github/workflows/solaris-build.yml
vendored
@@ -1,49 +0,0 @@
|
||||
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
59
.github/workflows/ubuntu-22.04-build.yml
vendored
@@ -1,59 +0,0 @@
|
||||
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
55
.github/workflows/ubuntu-build.yml
vendored
@@ -1,55 +0,0 @@
|
||||
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
|
||||
21
Makefile.in
21
Makefile.in
@@ -57,13 +57,12 @@ 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) t_chmod_secure$(EXEEXT) \
|
||||
t_secure_relpath$(EXEEXT) wildtest$(EXEEXT) simdtest$(EXEEXT)
|
||||
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(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 t_chmod_secure.o t_secure_relpath.o trimslash.o wildtest.o
|
||||
CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest.o
|
||||
|
||||
# note that the -I. is needed to handle config.h when using VPATH
|
||||
.c.o:
|
||||
@@ -179,14 +178,6 @@ 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
|
||||
|
||||
@@ -343,14 +334,6 @@ 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
|
||||
|
||||
|
||||
70
NEWS.md
70
NEWS.md
@@ -1,3 +1,53 @@
|
||||
# NEWS for rsync 3.3.0 (UNRELEASED)
|
||||
|
||||
## Changes in this version:
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- Fixed a bug with `--sparse --inplace` where a trailing gap in the source
|
||||
file would not clear out the trailing data in the destination file.
|
||||
|
||||
- Fixed an buffer overflow in the checksum2 code if SHA1 is being used for
|
||||
the checksum2 algorithm.
|
||||
|
||||
- Fixed a string-comparison issue in the internal file-list code that affected
|
||||
tr_TR.utf-8.
|
||||
|
||||
- Add a backtick to the list of characters that the filename quoting needs to
|
||||
escape using backslashes.
|
||||
|
||||
- Make sure that a local transfer marks the sender side as trusted.
|
||||
|
||||
- Change the argv handling to work with a newer popt library -- one that likes
|
||||
to free more data than it used to.
|
||||
|
||||
- Rsync now calls `OpenSSL_add_all_algorithms()` when compiled against an older
|
||||
openssl library.
|
||||
|
||||
- Fixed a problem in the daemon auth for older protocols (29 and before) if the
|
||||
openssl library is being used to compute md4 checksums.
|
||||
|
||||
- Fixed `rsync -VV` on Cygwin -- it needed a flush of stdout.
|
||||
|
||||
### ENHANCEMENTS:
|
||||
|
||||
- Enhanced rrsync with the `-no-overwrite` option that allows you to ensure
|
||||
that existing files on your restricted but writable directory can't be
|
||||
modified.
|
||||
|
||||
- Enhanced the manpages to mark links with .UR & .UE. If your nroff doesn't
|
||||
support these idioms, touch the file `.md2man-force` in the source directory
|
||||
so that `md-convert` gets called with the `--force-link-text` option, and
|
||||
that should ensure that your manpages are still readable even with the
|
||||
ignored markup.
|
||||
|
||||
- Some manpage improvements on the handling of [global] modules.
|
||||
|
||||
- Changed the mapfrom & mapto perl scripts (in the support dir) into a single
|
||||
python script named idmap. Converted a couple more perl scripts into python.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# NEWS for rsync 3.2.7 (20 Oct 2022)
|
||||
|
||||
## Changes in this version:
|
||||
@@ -217,9 +267,10 @@
|
||||
- A new form of arg protection was added that works similarly to the older
|
||||
`--protect-args` ([`-s`](rsync.1#opt)) option but in a way that avoids
|
||||
breaking things like rrsync (the restricted rsync script): rsync now uses
|
||||
backslash escaping for sending "shell-active" characters to the remote
|
||||
shell. This includes spaces, so fetching a remote file via a simple quoted
|
||||
filename value now works by default without any extra quoting:
|
||||
backslash escaping for sending "shell-active" characters to the remote shell
|
||||
(such as `$(){}<>#&` and others). This includes spaces, so fetching a remote
|
||||
file via a quoted filename value now works by default without any extra
|
||||
quoting:
|
||||
|
||||
```shell
|
||||
rsync -aiv host:'a simple file.pdf' .
|
||||
@@ -227,10 +278,14 @@
|
||||
|
||||
Wildcards are not escaped in filename args, but they are escaped in options
|
||||
like the [`--suffix`](rsync.1#opt) and [`--usermap`](rsync.1#opt) values.
|
||||
If your rsync script depends on the old arg-splitting behavior, either run
|
||||
it with the [`--old-args`](rsync.1#opt) option or `export RSYNC_OLD_ARGS=1`
|
||||
in the script's environment. See also the [ADVANCED USAGE](rsync.1#)
|
||||
section of rsync's manpage for how to use a more modern arg style.
|
||||
|
||||
If a script depends on the old arg behavior (perhaps because it quotes or
|
||||
protects the args already, or perhaps because it expects arg splitting),
|
||||
there are two easy ways to get things going with a modern rsync: either
|
||||
`export RSYNC_OLD_ARGS=1` in the script's environment (perhaps in the script
|
||||
itself) or add the option [`--old-args`](rsync.1#opt) to the rsync commands
|
||||
that are run. See also the [ADVANCED USAGE](rsync.1#) section of rsync's
|
||||
manpage for how to use a more modern arg style.
|
||||
|
||||
- A long-standing bug was preventing rsync from figuring out the current
|
||||
locale's decimal point character, which made rsync always output numbers
|
||||
@@ -4692,6 +4747,7 @@
|
||||
|
||||
| RELEASE DATE | VER. | DATE OF COMMIT\* | PROTOCOL |
|
||||
|--------------|--------|------------------|-------------|
|
||||
| ?? May 2023 | 3.3.0 | | 31 |
|
||||
| 20 Oct 2022 | 3.2.7 | | 31 |
|
||||
| 09 Sep 2022 | 3.2.6 | | 31 |
|
||||
| 14 Aug 2022 | 3.2.5 | | 31 |
|
||||
|
||||
2
access.c
2
access.c
@@ -99,7 +99,7 @@ static void make_mask(char *mask, int plen, int addrlen)
|
||||
return;
|
||||
}
|
||||
|
||||
static int match_address(const char *addr, char *tok)
|
||||
static int match_address(const char *addr, const char *tok)
|
||||
{
|
||||
char *p;
|
||||
struct addrinfo hints, *resa, *rest;
|
||||
|
||||
4
acls.c
4
acls.c
@@ -697,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_bounded(f, 0, MAX_WIRE_ACL_COUNT, "ACL count");
|
||||
int i, count = read_varint(f);
|
||||
|
||||
ent->idas = count ? new_array(id_access, count) : NULL;
|
||||
ent->count = count;
|
||||
@@ -713,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 && !numeric_ids)
|
||||
if (inc_recurse && am_root && !numeric_ids)
|
||||
id = match_uid(id);
|
||||
} else {
|
||||
if (inc_recurse && (!am_root || !numeric_ids))
|
||||
|
||||
14
backup.c
14
backup.c
@@ -39,7 +39,7 @@ static int validate_backup_dir(void)
|
||||
{
|
||||
STRUCT_STAT st;
|
||||
|
||||
if (do_lstat_at(backup_dir_buf, &st) < 0) {
|
||||
if (do_lstat(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_at(backup_dir_buf, ACCESSPERMS) < 0) {
|
||||
while (do_mkdir(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_at(from, to) == 0) {
|
||||
if (do_link(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_at(from, to) == 0) {
|
||||
if (do_rename(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_at(buf, &bakst) == 0) {
|
||||
if (do_lstat(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_at(buf, file->mode, sx.st.st_rdev) < 0)
|
||||
if (do_mknod(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_at(sl, buf) < 0)
|
||||
if (do_symlink(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);
|
||||
|
||||
18
checksum.c
18
checksum.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2004-2022 Wayne Davison
|
||||
* Copyright (C) 2004-2023 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -154,7 +154,7 @@ static const EVP_MD *csum_evp_md(struct name_num_item *nni)
|
||||
emd = NULL;
|
||||
else
|
||||
#endif
|
||||
emd = EVP_get_digestbyname(nni->name);
|
||||
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");
|
||||
@@ -176,7 +176,7 @@ void parse_checksum_choice(int final_call)
|
||||
if (valid_checksums.negotiated_nni)
|
||||
xfer_sum_nni = file_sum_nni = valid_checksums.negotiated_nni;
|
||||
else {
|
||||
const char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
|
||||
char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
|
||||
if (cp) {
|
||||
xfer_sum_nni = parse_csum_name(checksum_choice, cp - checksum_choice);
|
||||
file_sum_nni = parse_csum_name(cp+1, -1);
|
||||
@@ -300,6 +300,7 @@ uint32 get_checksum1(char *buf1, int32 len)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The "sum" buffer must be at least MAX_DIGEST_LEN bytes! */
|
||||
void get_checksum2(char *buf, int32 len, char *sum)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
@@ -365,8 +366,9 @@ void get_checksum2(char *buf, int32 len, char *sum)
|
||||
|
||||
mdfour_begin(&m);
|
||||
|
||||
if (len > len1 || !buf1) {
|
||||
free(buf1);
|
||||
if (len > len1) {
|
||||
if (buf1)
|
||||
free(buf1);
|
||||
buf1 = new_array(char, len+4);
|
||||
len1 = len;
|
||||
}
|
||||
@@ -404,7 +406,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
|
||||
int32 remainder;
|
||||
int fd;
|
||||
|
||||
fd = do_open_checklinks(fname);
|
||||
fd = do_open(fname, O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
memset(sum, 0, file_sum_len);
|
||||
return;
|
||||
@@ -786,6 +788,10 @@ void init_checksum_choices()
|
||||
if (initialized_choices)
|
||||
return;
|
||||
|
||||
#if defined USE_OPENSSL && OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif
|
||||
|
||||
#if defined SUPPORT_XXH3 || defined USE_OPENSSL
|
||||
for (nni = valid_checksums.list; nni->name; nni++)
|
||||
verify_digest(nni, True);
|
||||
|
||||
@@ -198,7 +198,7 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
|
||||
switch_step++;
|
||||
|
||||
if (cleanup_fname)
|
||||
do_unlink_at(cleanup_fname);
|
||||
do_unlink(cleanup_fname);
|
||||
if (exit_code)
|
||||
kill_all(SIGUSR1);
|
||||
if (cleanup_pid && cleanup_pid == getpid()) {
|
||||
|
||||
@@ -167,7 +167,7 @@ int read_proxy_protocol_header(int fd)
|
||||
char sig[PROXY_V2_SIG_SIZE];
|
||||
char ver_cmd;
|
||||
char fam;
|
||||
unsigned char len[2];
|
||||
char len[2];
|
||||
union {
|
||||
struct {
|
||||
char src_addr[4];
|
||||
|
||||
@@ -30,7 +30,6 @@ 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;
|
||||
@@ -39,7 +38,6 @@ 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;
|
||||
@@ -978,14 +976,11 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
}
|
||||
|
||||
if (use_chroot) {
|
||||
/* Cache timezone data before chroot makes /etc/localtime inaccessible */
|
||||
tzset();
|
||||
if (chroot(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;
|
||||
}
|
||||
|
||||
@@ -1008,15 +1003,6 @@ 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])) {
|
||||
@@ -1312,49 +1298,13 @@ 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. */
|
||||
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;
|
||||
|
||||
10
compat.c
10
compat.c
@@ -131,7 +131,7 @@ static const char *client_info;
|
||||
* of that protocol for it to be advertised as available. */
|
||||
static void check_sub_protocol(void)
|
||||
{
|
||||
const char *dot;
|
||||
char *dot;
|
||||
int their_protocol, their_sub;
|
||||
int our_sub = get_subprotocol_version();
|
||||
|
||||
@@ -414,7 +414,7 @@ static const char *getenv_nstr(int ntype)
|
||||
env_str = ntype == NSTR_COMPRESS ? "zlib" : protocol_version >= 30 ? "md5" : "md4";
|
||||
|
||||
if (am_server && env_str) {
|
||||
const char *cp = strchr(env_str, '&');
|
||||
char *cp = strchr(env_str, '&');
|
||||
if (cp)
|
||||
env_str = cp + 1;
|
||||
}
|
||||
@@ -834,6 +834,8 @@ void output_daemon_greeting(int f_out, int am_client)
|
||||
char tmpbuf[MAX_NSTR_STRLEN];
|
||||
int our_sub = get_subprotocol_version();
|
||||
|
||||
init_checksum_choices();
|
||||
|
||||
get_default_nno_list(&valid_auth_checksums, tmpbuf, MAX_NSTR_STRLEN, '\0');
|
||||
|
||||
io_printf(f_out, "@RSYNCD: %d.%d %s\n", protocol_version, our_sub, tmpbuf);
|
||||
@@ -873,8 +875,10 @@ void negotiate_daemon_auth(int f_out, int am_client)
|
||||
}
|
||||
}
|
||||
am_server = save_am_server;
|
||||
if (md4_is_old && valid_auth_checksums.negotiated_nni->num == CSUM_MD4)
|
||||
if (md4_is_old && valid_auth_checksums.negotiated_nni->num == CSUM_MD4) {
|
||||
valid_auth_checksums.negotiated_nni->num = CSUM_MD4_OLD;
|
||||
valid_auth_checksums.negotiated_nni->flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int get_subprotocol_version()
|
||||
|
||||
@@ -432,10 +432,10 @@ AH_TEMPLATE([USE_OPENSSL],
|
||||
if test x"$enable_openssl" != x"no"; then
|
||||
if test x"$ac_cv_header_openssl_md4_h" = x"yes" && test x"$ac_cv_header_openssl_md5_h" = x"yes"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_SEARCH_LIBS(MD5_Init, crypto,
|
||||
AC_SEARCH_LIBS(EVP_MD_CTX_copy, crypto,
|
||||
[AC_DEFINE(USE_OPENSSL)
|
||||
enable_openssl=yes],
|
||||
[err_msg="$err_msg$nl- Failed to find MD5_Init function in openssl crypto lib.";
|
||||
[err_msg="$err_msg$nl- Failed to find EVP_MD_CTX_copy function in openssl crypto lib.";
|
||||
no_lib="$no_lib openssl"])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
@@ -1390,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_SEARCH_LIBS(getxattr,attr)
|
||||
AC_CHECK_LIB(attr,getxattr)
|
||||
;;
|
||||
darwin*)
|
||||
AC_MSG_RESULT(Using OS X xattrs)
|
||||
|
||||
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_at(fname, fp->mode | S_IWUSR);
|
||||
do_chmod(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_at(fbuf, mode | S_IWUSR);
|
||||
do_chmod(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_at(fbuf) == 0;
|
||||
ok = do_rmdir(fbuf) == 0;
|
||||
} else {
|
||||
if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
|
||||
what = "make_backup";
|
||||
|
||||
@@ -720,8 +720,7 @@ static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
|
||||
parent_dirscan = True;
|
||||
while (*y) {
|
||||
char save[MAXPATHLEN];
|
||||
/* copylen is strlen(y) which is < MAXPATHLEN. +1 for \0 */
|
||||
size_t copylen = strlcpy(save, y, MAXPATHLEN) + 1;
|
||||
strlcpy(save, y, MAXPATHLEN);
|
||||
*y = '\0';
|
||||
dirbuf_len = y - dirbuf;
|
||||
strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
|
||||
@@ -735,7 +734,7 @@ static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
|
||||
lp->head = NULL;
|
||||
}
|
||||
lp->tail = NULL;
|
||||
strlcpy(y, save, copylen);
|
||||
strlcpy(y, save, MAXPATHLEN);
|
||||
while ((*x++ = *y++) != '/') {}
|
||||
}
|
||||
parent_dirscan = False;
|
||||
@@ -904,7 +903,7 @@ 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;
|
||||
const char *p, *pattern = ex->pattern;
|
||||
char *p, *pattern = ex->pattern;
|
||||
const char *strings[16]; /* more than enough */
|
||||
const char *name = fname + (*fname == '/');
|
||||
|
||||
|
||||
46
fileio.c
46
fileio.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Tridgell
|
||||
* Copyright (C) 2002 Martin Pool
|
||||
* Copyright (C) 2004-2020 Wayne Davison
|
||||
* Copyright (C) 2004-2023 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -40,30 +40,34 @@ OFF_T preallocated_len = 0;
|
||||
static OFF_T sparse_seek = 0;
|
||||
static OFF_T sparse_past_write = 0;
|
||||
|
||||
int sparse_end(int f, OFF_T size)
|
||||
int sparse_end(int f, OFF_T size, int updating_basis_or_equiv)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
sparse_past_write = 0;
|
||||
|
||||
if (!sparse_seek)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_FTRUNCATE
|
||||
ret = do_ftruncate(f, size);
|
||||
#else
|
||||
if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
|
||||
ret = -1;
|
||||
else {
|
||||
do {
|
||||
ret = write(f, "", 1);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
ret = ret <= 0 ? -1 : 0;
|
||||
}
|
||||
if (updating_basis_or_equiv) {
|
||||
if (sparse_seek && do_punch_hole(f, sparse_past_write, sparse_seek) < 0)
|
||||
ret = -1;
|
||||
#ifdef HAVE_FTRUNCATE /* A compilation formality -- in-place requires ftruncate() */
|
||||
else /* Just in case the original file was longer */
|
||||
ret = do_ftruncate(f, size);
|
||||
#endif
|
||||
} else if (sparse_seek) {
|
||||
#ifdef HAVE_FTRUNCATE
|
||||
ret = do_ftruncate(f, size);
|
||||
#else
|
||||
if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
|
||||
ret = -1;
|
||||
else {
|
||||
do {
|
||||
ret = write(f, "", 1);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
sparse_seek = 0;
|
||||
ret = ret <= 0 ? -1 : 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
sparse_past_write = sparse_seek = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
36
flist.c
36
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-2022 Wayne Davison
|
||||
* Copyright (C) 2002-2023 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -840,9 +840,9 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
}
|
||||
if (xflags & XMIT_MOD_NSEC)
|
||||
#ifndef CAN_SET_NSEC
|
||||
(void)read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
(void)read_varint(f);
|
||||
#else
|
||||
modtime_nsec = read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
modtime_nsec = read_varint(f);
|
||||
else
|
||||
modtime_nsec = 0;
|
||||
#endif
|
||||
@@ -861,19 +861,8 @@ 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
|
||||
@@ -1401,7 +1390,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
|
||||
|
||||
if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
|
||||
if (st.st_size == 0) {
|
||||
int fd = do_open_checklinks(fname);
|
||||
int fd = do_open(fname, O_RDONLY, 0);
|
||||
if (fd >= 0) {
|
||||
st.st_size = get_device_size(fd, fname);
|
||||
close(fd);
|
||||
@@ -2378,7 +2367,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
|
||||
}
|
||||
|
||||
dirlen = dir ? strlen(dir) : 0;
|
||||
if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
|
||||
if (dirlen != lastdir_len || (dirlen && memcmp(lastdir, dir, dirlen) != 0)) {
|
||||
if (!change_pathname(NULL, dir, -dirlen))
|
||||
goto bad_path;
|
||||
lastdir = pathname;
|
||||
@@ -2595,19 +2584,6 @@ 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);
|
||||
|
||||
|
||||
60
generator.c
60
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-2022 Wayne Davison
|
||||
* Copyright (C) 2003-2023 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -229,13 +229,11 @@ static int read_delay_line(char *buf, int *flags_p)
|
||||
*flags_p = 0;
|
||||
|
||||
if (sscanf(bp, "%x ", &mode) != 1) {
|
||||
goto invalid_data;
|
||||
invalid_data:
|
||||
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
|
||||
return -1;
|
||||
}
|
||||
past_space = strchr(bp, ' ');
|
||||
if (!past_space) {
|
||||
goto invalid_data;
|
||||
}
|
||||
past_space++;
|
||||
past_space = strchr(bp, ' ') + 1;
|
||||
len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
|
||||
read_pos = j + 1;
|
||||
|
||||
@@ -249,10 +247,6 @@ 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)
|
||||
@@ -789,7 +783,7 @@ static int generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy)
|
||||
for (i = 0; i < sum.count; i++) {
|
||||
int32 n1 = (int32)MIN(len, (OFF_T)sum.blength);
|
||||
char *map = map_ptr(mapbuf, offset, n1);
|
||||
char sum2[SUM_LENGTH];
|
||||
char sum2[MAX_DIGEST_LEN];
|
||||
uint32 sum1;
|
||||
|
||||
len -= n1;
|
||||
@@ -990,7 +984,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_at(fname) < 0 && errno != ENOENT)
|
||||
if (do_unlink(fname) < 0 && errno != ENOENT)
|
||||
goto got_nothing_for_ya;
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
@@ -1118,7 +1112,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_at(cmpbuf, fname) < 0) {
|
||||
if (do_link(cmpbuf, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to hard-link %s with %s",
|
||||
cmpbuf, fname);
|
||||
@@ -1321,7 +1315,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
}
|
||||
if (relative_paths && !implied_dirs && file->mode != 0
|
||||
&& do_stat_at(dn, &sx.st) < 0) {
|
||||
&& do_stat(dn, &sx.st) < 0) {
|
||||
if (dry_run)
|
||||
goto parent_is_dry_missing;
|
||||
if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) {
|
||||
@@ -1433,7 +1427,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_at(fname, (file->mode|added_perms) & 0700) == 0)
|
||||
if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0)
|
||||
file->flags |= FLAG_DIR_CREATED;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1475,10 +1469,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_at(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
|
||||
if (real_ret != 0 && do_mkdir(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_at(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
|| (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"recv_generator: mkdir %s failed",
|
||||
full_fname(fname));
|
||||
@@ -1505,7 +1499,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_at(fname, mode) < 0) {
|
||||
if (do_chmod(fname, mode) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to modify permissions on %s",
|
||||
full_fname(fname));
|
||||
@@ -1804,7 +1798,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
|
||||
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)
|
||||
if ((fd = do_open(fnamecmp, O_RDONLY, 0)) >= 0)
|
||||
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
|
||||
}
|
||||
|
||||
@@ -1814,7 +1808,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
;
|
||||
else if (quick_check_ok(FT_REG, fnamecmp, file, &sx.st)) {
|
||||
if (partialptr) {
|
||||
do_unlink_at(partialptr);
|
||||
do_unlink(partialptr);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT | maybe_ATTRS_ACCURATE_TIME);
|
||||
@@ -1873,7 +1867,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
if (fd < 0 && (fd = do_open_checklinks(fnamecmp)) < 0) {
|
||||
if (fd < 0 && (fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
|
||||
rsyserr(FERROR, errno, "failed to open %s, continuing",
|
||||
full_fname(fnamecmp));
|
||||
pretend_missing:
|
||||
@@ -1902,7 +1896,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
back_file = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
if ((f_copy = do_open_at(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
if ((f_copy = do_open(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;
|
||||
@@ -2022,7 +2016,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
|
||||
if (slnk) {
|
||||
#ifdef SUPPORT_LINKS
|
||||
if (do_symlink_at(slnk, create_name) < 0) {
|
||||
if (do_symlink(slnk, create_name) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
|
||||
full_fname(create_name), slnk);
|
||||
return 0;
|
||||
@@ -2038,7 +2032,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
return 0;
|
||||
#endif
|
||||
} else {
|
||||
if (do_mknod_at(create_name, file->mode, rdev) < 0) {
|
||||
if (do_mknod(create_name, file->mode, rdev) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "mknod %s failed",
|
||||
full_fname(create_name));
|
||||
return 0;
|
||||
@@ -2046,14 +2040,10 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
}
|
||||
|
||||
if (!skip_atomic) {
|
||||
if (do_rename_at(tmpname, fname) < 0) {
|
||||
char *full_tmpname = strdup(full_fname(tmpname));
|
||||
if (full_tmpname == NULL)
|
||||
out_of_memory("atomic_create");
|
||||
if (do_rename(tmpname, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
|
||||
full_tmpname, full_fname(fname));
|
||||
free(full_tmpname);
|
||||
do_unlink_at(tmpname);
|
||||
full_fname(tmpname), full_fname(fname));
|
||||
do_unlink(tmpname);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2117,7 +2107,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
|
||||
continue;
|
||||
fname = f_name(file, NULL);
|
||||
if (fix_dir_perms)
|
||||
do_chmod_at(fname, file->mode);
|
||||
do_chmod(fname, file->mode);
|
||||
if (need_retouch_dir_times) {
|
||||
STRUCT_STAT st;
|
||||
if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
|
||||
@@ -2152,8 +2142,6 @@ 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)
|
||||
@@ -2182,8 +2170,6 @@ 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)
|
||||
|
||||
4
hlink.c
4
hlink.c
@@ -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 (*)(const void*, const void*))hlink_compare_gnum);
|
||||
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)()) hlink_compare_gnum);
|
||||
|
||||
for (from = 0; from < ndx_count; from++) {
|
||||
file = hlink_flist->sorted[ndx_list[from]];
|
||||
@@ -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_at(oldname, fname) < 0) {
|
||||
if (do_link(oldname, fname) < 0) {
|
||||
enum logcode code;
|
||||
if (terse) {
|
||||
if (!INFO_GTE(NAME, 1))
|
||||
|
||||
64
io.c
64
io.c
@@ -55,7 +55,6 @@ 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;
|
||||
@@ -1090,9 +1089,6 @@ 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]);
|
||||
@@ -1161,8 +1157,8 @@ void set_io_timeout(int secs)
|
||||
|
||||
static void check_for_d_option_error(const char *msg)
|
||||
{
|
||||
static const char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
|
||||
const char *colon;
|
||||
static char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
|
||||
char *colon;
|
||||
int saw_d = 0;
|
||||
|
||||
if (*msg != 'r'
|
||||
@@ -1868,45 +1864,6 @@ 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
|
||||
@@ -2013,21 +1970,6 @@ 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",
|
||||
@@ -2035,7 +1977,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 > xfer_sum_len) {
|
||||
if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
|
||||
rprintf(FERROR, "Invalid checksum length %d [%s]\n",
|
||||
sum->s2length, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define LATEST_YEAR "2022"
|
||||
#define LATEST_YEAR "2023"
|
||||
|
||||
@@ -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)(const char*, const char*, int); /* called if malloc fails */
|
||||
void (*bomb)(); /* called if malloc fails */
|
||||
int flags;
|
||||
|
||||
/* statistical data */
|
||||
|
||||
@@ -178,7 +178,7 @@ static char *expand_vars(const char *str)
|
||||
|
||||
for (t = buf, f = str; bufsize && *f; ) {
|
||||
if (*f == '%' && isUpper(f+1)) {
|
||||
const char *percent = strchr(f+1, '%');
|
||||
char *percent = strchr(f+1, '%');
|
||||
if (percent && percent - f < bufsize) {
|
||||
char *val;
|
||||
strlcpy(t, f+1, percent - f);
|
||||
|
||||
12
log.c
12
log.c
@@ -456,17 +456,11 @@ 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());
|
||||
|
||||
if (len < sizeof buf) {
|
||||
va_start(ap, format);
|
||||
len += vsnprintf(buf + len, sizeof buf - len, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
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,
|
||||
|
||||
42
main.c
42
main.c
@@ -89,6 +89,8 @@ extern int backup_dir_len;
|
||||
extern int basis_dir_cnt;
|
||||
extern int default_af_hint;
|
||||
extern int stdout_format_has_i;
|
||||
extern int trust_sender_filter;
|
||||
extern int trust_sender_args;
|
||||
extern struct stats stats;
|
||||
extern char *stdout_format;
|
||||
extern char *logfile_format;
|
||||
@@ -237,11 +239,11 @@ void write_del_stats(int f)
|
||||
|
||||
void read_del_stats(int 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");
|
||||
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);
|
||||
}
|
||||
|
||||
static void become_copy_as_user()
|
||||
@@ -392,18 +394,9 @@ 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 = ", ";
|
||||
}
|
||||
}
|
||||
@@ -1390,15 +1383,6 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
|
||||
return MAX(exit_code, exit_code2);
|
||||
}
|
||||
|
||||
static void dup_argv(char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; argv[i]; i++)
|
||||
argv[i] = strdup(argv[i]);
|
||||
}
|
||||
|
||||
|
||||
/* Start a client for either type of remote connection. Work out
|
||||
* whether the arguments request a remote shell or rsyncd connection,
|
||||
* and call the appropriate connection function, then run_client.
|
||||
@@ -1414,10 +1398,6 @@ static int start_client(int argc, char *argv[])
|
||||
int ret;
|
||||
pid_t pid;
|
||||
|
||||
/* Don't clobber argv[] so that ps(1) can still show the right
|
||||
* command line. */
|
||||
dup_argv(argv);
|
||||
|
||||
if (!read_batch) { /* for read_batch, NO source is specified */
|
||||
char *path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
|
||||
if (path) { /* source is remote */
|
||||
@@ -1493,8 +1473,10 @@ static int start_client(int argc, char *argv[])
|
||||
}
|
||||
|
||||
/* A local transfer doesn't unbackslash anything, so leave the args alone. */
|
||||
if (local_server)
|
||||
if (local_server) {
|
||||
old_style_args = 2;
|
||||
trust_sender_args = trust_sender_filter = 1;
|
||||
}
|
||||
|
||||
if (!rsync_port && remote_argc && !**remote_argv) /* Turn an empty arg into a dot dir. */
|
||||
*remote_argv = ".";
|
||||
@@ -1577,10 +1559,6 @@ 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)) {
|
||||
|
||||
15
match.c
15
match.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
* Copyright (C) 2003-2023 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -142,14 +142,11 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
{
|
||||
OFF_T offset, aligned_offset, end;
|
||||
int32 k, want_i, aligned_i, backup;
|
||||
char sum2[SUM_LENGTH];
|
||||
char sum2[MAX_DIGEST_LEN];
|
||||
uint32 s1, s2, sum;
|
||||
int more;
|
||||
schar *map;
|
||||
|
||||
// prevent possible memory leaks
|
||||
memset(sum2, 0, sizeof sum2);
|
||||
|
||||
/* want_i is used to encourage adjacent matches, allowing the RLL
|
||||
* coding of the output to work more efficiently. */
|
||||
want_i = 0;
|
||||
@@ -235,7 +232,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
done_csum2 = 1;
|
||||
}
|
||||
|
||||
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0) {
|
||||
if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) {
|
||||
false_alarms++;
|
||||
continue;
|
||||
}
|
||||
@@ -255,7 +252,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, sum2_at(s, aligned_i), s->s2length) != 0)
|
||||
|| memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0)
|
||||
goto check_want_i;
|
||||
i = aligned_i;
|
||||
}
|
||||
@@ -274,7 +271,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, sum2_at(s, i), s->s2length) != 0)
|
||||
if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0)
|
||||
goto check_want_i;
|
||||
/* OK, we have a re-alignment match. Bump the offset
|
||||
* forward to the new match point. */
|
||||
@@ -293,7 +290,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, sum2_at(s, want_i), s->s2length) == 0) {
|
||||
&& memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) {
|
||||
/* we've found an adjacent match - the RLL coder
|
||||
* will be happy */
|
||||
i = want_i;
|
||||
|
||||
@@ -8,6 +8,7 @@ fi
|
||||
inname="$1"
|
||||
srcdir=`dirname "$0"`
|
||||
flagfile="$srcdir/.md2man-works"
|
||||
force_flagfile="$srcdir/.md2man-force"
|
||||
|
||||
if [ ! -f "$flagfile" ]; then
|
||||
# We test our smallest manpage just to see if the python setup works.
|
||||
@@ -32,4 +33,10 @@ if [ ! -f "$flagfile" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
"$srcdir/md-convert" "$srcdir/$inname"
|
||||
if [ -f "$force_flagfile" ]; then
|
||||
opt='--force-link-text'
|
||||
else
|
||||
opt=''
|
||||
fi
|
||||
|
||||
"$srcdir/md-convert" $opt "$srcdir/$inname"
|
||||
|
||||
32
md-convert
32
md-convert
@@ -276,7 +276,10 @@ class TransformHtml(HTMLParser):
|
||||
bad_hashtags = set(),
|
||||
latest_targets = [ ],
|
||||
opt_prefix = 'opt',
|
||||
a_href = None,
|
||||
a_href_external = False,
|
||||
a_txt_start = None,
|
||||
after_a_tag = False,
|
||||
target_suf = '',
|
||||
)
|
||||
|
||||
@@ -315,6 +318,13 @@ class TransformHtml(HTMLParser):
|
||||
for bad in st.referenced_hashtags - st.created_hashtags:
|
||||
warn('Unknown hashtag link in', self.fn + ':', '#' + bad)
|
||||
|
||||
def handle_UE(self):
|
||||
st = self.state
|
||||
if st.txt.startswith(('.', ',', '!', '?', ';', ':')):
|
||||
st.man_out[-1] = ".UE " + st.txt[0] + "\n"
|
||||
st.txt = st.txt[1:]
|
||||
st.after_a_tag = False
|
||||
|
||||
def handle_starttag(self, tag, attrs_list):
|
||||
st = self.state
|
||||
if args.debug:
|
||||
@@ -387,13 +397,20 @@ class TransformHtml(HTMLParser):
|
||||
for var, val in attrs_list:
|
||||
if var == 'href':
|
||||
if val.startswith(('https://', 'http://', 'mailto:', 'ftp:')):
|
||||
pass # nothing to check
|
||||
if st.after_a_tag:
|
||||
self.handle_UE()
|
||||
st.man_out.append(manify(st.txt.strip()) + "\n")
|
||||
st.man_out.append(".UR " + val + "\n")
|
||||
st.txt = ''
|
||||
st.a_href = val
|
||||
st.a_href_external = True
|
||||
elif '#' in val:
|
||||
pg, tgt = val.split('#', 1)
|
||||
if pg and pg not in VALID_PAGES or '#' in tgt:
|
||||
st.bad_hashtags.add(val)
|
||||
elif tgt in ('', 'opt', 'dopt'):
|
||||
st.a_href = val
|
||||
st.a_href_external = False
|
||||
elif pg == '':
|
||||
st.referenced_hashtags.add(tgt)
|
||||
if tgt in st.latest_targets:
|
||||
@@ -409,6 +426,8 @@ class TransformHtml(HTMLParser):
|
||||
st = self.state
|
||||
if args.debug:
|
||||
self.output_debug('END', (tag,))
|
||||
if st.after_a_tag:
|
||||
self.handle_UE()
|
||||
if tag in CONSUMES_TXT or st.dt_from == tag:
|
||||
txt = st.txt.strip()
|
||||
st.txt = ''
|
||||
@@ -473,7 +492,15 @@ class TransformHtml(HTMLParser):
|
||||
elif tag == 'hr':
|
||||
return
|
||||
elif tag == 'a':
|
||||
if st.a_href:
|
||||
if st.a_href_external:
|
||||
st.txt = st.txt.strip()
|
||||
if args.force_link_text or st.a_href != st.txt:
|
||||
st.man_out.append(manify(st.txt) + "\n")
|
||||
st.man_out.append(".UE\n") # This might get replaced with a punctuation version in handle_UE()
|
||||
st.after_a_tag = True
|
||||
st.a_href_external = False
|
||||
st.txt = ''
|
||||
elif st.a_href:
|
||||
atxt = st.txt[st.a_txt_start:]
|
||||
find = 'href="' + st.a_href + '"'
|
||||
for j in range(len(st.html_out)-1, 0, -1):
|
||||
@@ -612,6 +639,7 @@ if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Convert markdown into html and (optionally) nroff. Each input filename must have a .md suffix, which is changed to .html for the output filename. If the input filename ends with .num.md (e.g. foo.1.md) then a nroff file is also output with the input filename's .md suffix removed (e.g. foo.1).", add_help=False)
|
||||
parser.add_argument('--test', action='store_true', help="Just test the parsing without outputting any files.")
|
||||
parser.add_argument('--dest', metavar='DIR', help="Create files in DIR instead of the current directory.")
|
||||
parser.add_argument('--force-link-text', action='store_true', help="Don't remove the link text if it matches the link href. Useful when nroff doesn't understand .UR and .UE.")
|
||||
parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
parser.add_argument("mdfiles", metavar='FILE.md', nargs='+', help="One or more .md files to convert.")
|
||||
|
||||
183
options.c
183
options.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
|
||||
* Copyright (C) 2000, 2001, 2002 Martin Pool <mbp@samba.org>
|
||||
* Copyright (C) 2002-2022 Wayne Davison
|
||||
* Copyright (C) 2002-2023 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -113,20 +113,11 @@ 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;
|
||||
@@ -209,6 +200,7 @@ int remote_option_cnt = 0;
|
||||
const char **remote_options = NULL;
|
||||
const char *checksum_choice = NULL;
|
||||
const char *compress_choice = NULL;
|
||||
static const char *empty_argv[1];
|
||||
|
||||
int quiet = 0;
|
||||
int output_motd = 1;
|
||||
@@ -1164,7 +1156,7 @@ static time_t parse_time(const char *arg)
|
||||
{
|
||||
const char *cp;
|
||||
time_t val, now = time(NULL);
|
||||
struct tm t, tmp, *today = localtime_r(&now, &tmp);
|
||||
struct tm t, *today = localtime(&now);
|
||||
int in_date, old_mday, n;
|
||||
|
||||
memset(&t, 0, sizeof t);
|
||||
@@ -1356,7 +1348,7 @@ char *alt_dest_opt(int type)
|
||||
**/
|
||||
int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
{
|
||||
static poptContext pc;
|
||||
poptContext pc;
|
||||
const char *arg, **argv = *argv_p;
|
||||
int argc = *argc_p;
|
||||
int opt, want_dest_type;
|
||||
@@ -1376,15 +1368,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
|
||||
/* TODO: Call poptReadDefaultConfig; handle errors. */
|
||||
|
||||
/* The context leaks in case of an error, but if there's a
|
||||
* problem we always exit anyhow. */
|
||||
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");
|
||||
@@ -1426,7 +1410,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
strlcpy(err_buf,
|
||||
"Attempt to hack rsync thwarted!\n",
|
||||
sizeof err_buf);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
#ifdef ICONV_OPTION
|
||||
iconv_opt = NULL;
|
||||
@@ -1472,7 +1456,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"the --temp-dir path is WAY too long.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!daemon_opt) {
|
||||
@@ -1482,8 +1466,16 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
|
||||
*argv_p = argv = poptGetArgs(pc);
|
||||
*argc_p = argc = count_args(argv);
|
||||
argv = poptGetArgs(pc);
|
||||
argc = count_args(argv);
|
||||
if (!argc) {
|
||||
*argv_p = empty_argv;
|
||||
*argc_p = 0;
|
||||
} else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
|
||||
out_of_memory("parse_arguments");
|
||||
argv = *argv_p;
|
||||
poptFreeContext(pc);
|
||||
|
||||
am_starting_up = 0;
|
||||
daemon_opt = 0;
|
||||
am_daemon = 1;
|
||||
@@ -1538,7 +1530,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
case 'a':
|
||||
if (refused_archive_part) {
|
||||
create_refuse_error(refused_archive_part);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (!recurse) /* preserve recurse == 2 */
|
||||
recurse = 1;
|
||||
@@ -1608,7 +1600,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
case 'P':
|
||||
if (refused_partial || refused_progress) {
|
||||
create_refuse_error(refused_partial ? refused_partial : refused_progress);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
do_progress = 1;
|
||||
keep_partial = 1;
|
||||
@@ -1643,7 +1635,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (*arg != '-') {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"Remote option must start with a dash: %s\n", arg);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (remote_option_cnt+2 >= remote_option_alloc) {
|
||||
remote_option_alloc += 16;
|
||||
@@ -1685,27 +1677,27 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
ssize_t size;
|
||||
arg = poptGetOptArg(pc);
|
||||
if ((size = parse_size_arg(arg, 'b', "block-size", 0, max_blength, False)) < 0)
|
||||
return 0;
|
||||
goto cleanup;
|
||||
block_size = (int32)size;
|
||||
break;
|
||||
}
|
||||
|
||||
case OPT_MAX_SIZE:
|
||||
if ((max_size = parse_size_arg(max_size_arg, 'b', "max-size", 0, -1, False)) < 0)
|
||||
return 0;
|
||||
goto cleanup;
|
||||
max_size_arg = strdup(do_big_num(max_size, 0, NULL));
|
||||
break;
|
||||
|
||||
case OPT_MIN_SIZE:
|
||||
if ((min_size = parse_size_arg(min_size_arg, 'b', "min-size", 0, -1, False)) < 0)
|
||||
return 0;
|
||||
goto cleanup;
|
||||
min_size_arg = strdup(do_big_num(min_size, 0, NULL));
|
||||
break;
|
||||
|
||||
case OPT_BWLIMIT: {
|
||||
ssize_t size = parse_size_arg(bwlimit_arg, 'K', "bwlimit", 512, -1, True);
|
||||
if (size < 0)
|
||||
return 0;
|
||||
goto cleanup;
|
||||
bwlimit_arg = strdup(do_big_num(size, 0, NULL));
|
||||
bwlimit = (size + 512) / 1024;
|
||||
break;
|
||||
@@ -1734,7 +1726,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"ERROR: the %s option conflicts with the %s option\n",
|
||||
alt_dest_opt(want_dest_type), alt_dest_opt(0));
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
alt_dest_type = want_dest_type;
|
||||
|
||||
@@ -1742,7 +1734,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"ERROR: at most %d %s args may be specified\n",
|
||||
MAX_BASIS_DIRS, alt_dest_opt(0));
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
/* We defer sanitizing this arg until we know what
|
||||
* our destination directory is going to be. */
|
||||
@@ -1755,7 +1747,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"Invalid argument passed to --chmod (%s)\n",
|
||||
arg);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1774,11 +1766,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (usermap_via_chown) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--usermap conflicts with prior --chown.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"You can only specify --usermap once.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
usermap = (char *)poptGetOptArg(pc);
|
||||
usermap_via_chown = False;
|
||||
@@ -1790,11 +1782,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (groupmap_via_chown) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--groupmap conflicts with prior --chown.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"You can only specify --groupmap once.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
groupmap = (char *)poptGetOptArg(pc);
|
||||
groupmap_via_chown = False;
|
||||
@@ -1813,11 +1805,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (!usermap_via_chown) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--chown conflicts with prior --usermap.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"You can only specify a user-affecting --chown once.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (asprintf(&usermap, "*:%.*s", len, chown) < 0)
|
||||
out_of_memory("parse_arguments");
|
||||
@@ -1829,11 +1821,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (!groupmap_via_chown) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--chown conflicts with prior --groupmap.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"You can only specify a group-affecting --chown once.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (asprintf(&groupmap, "*:%s", arg) < 0)
|
||||
out_of_memory("parse_arguments");
|
||||
@@ -1861,7 +1853,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf,sizeof(err_buf),
|
||||
"ACLs are not supported on this %s\n",
|
||||
am_server ? "server" : "client");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
#endif
|
||||
|
||||
case 'X':
|
||||
@@ -1872,7 +1864,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf,sizeof(err_buf),
|
||||
"extended attributes are not supported on this %s\n",
|
||||
am_server ? "server" : "client");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
#endif
|
||||
|
||||
case OPT_STOP_AFTER: {
|
||||
@@ -1881,7 +1873,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
stop_at_utime = time(NULL);
|
||||
if ((val = atol(arg) * 60) <= 0 || LONG_MAX - val < stop_at_utime || (long)(time_t)val != val) {
|
||||
snprintf(err_buf, sizeof err_buf, "invalid --stop-after value: %s\n", arg);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
stop_at_utime += val;
|
||||
break;
|
||||
@@ -1892,11 +1884,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
arg = poptGetOptArg(pc);
|
||||
if ((stop_at_utime = parse_time(arg)) == (time_t)-1) {
|
||||
snprintf(err_buf, sizeof err_buf, "invalid --stop-at format: %s\n", arg);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (stop_at_utime <= time(NULL)) {
|
||||
snprintf(err_buf, sizeof err_buf, "--stop-at time is not in the future: %s\n", arg);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@@ -1914,7 +1906,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
else {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--stderr mode \"%s\" is not one of errors, all, or client\n", arg);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
saw_stderr_opt = 1;
|
||||
break;
|
||||
@@ -1925,13 +1917,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
* turned this option off. */
|
||||
if (opt >= OPT_REFUSED_BASE) {
|
||||
create_refuse_error(opt);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
snprintf(err_buf, sizeof err_buf, "%s%s: %s\n",
|
||||
am_server ? "on remote machine: " : "",
|
||||
poptBadOption(pc, POPT_BADOPTION_NOALIAS),
|
||||
poptStrerror(opt));
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1951,7 +1943,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (max_alloc_arg) {
|
||||
ssize_t size = parse_size_arg(max_alloc_arg, 'B', "max-alloc", 1024*1024, -1, True);
|
||||
if (size < 0)
|
||||
return 0;
|
||||
goto cleanup;
|
||||
max_alloc = size;
|
||||
}
|
||||
|
||||
@@ -1965,7 +1957,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (protect_args > 0) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--secluded-args conflicts with --old-args.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
protect_args = 0;
|
||||
}
|
||||
@@ -2010,7 +2002,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
do_compression = CPRES_AUTO;
|
||||
if (do_compression && refused_compress) {
|
||||
create_refuse_error(refused_compress);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2035,7 +2027,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
default:
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"Invalid --outbuf setting -- specify N, L, or B.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
setvbuf(stdout, (char *)NULL, mode, 0);
|
||||
}
|
||||
@@ -2063,7 +2055,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
}
|
||||
if (refused_no_iconv && !iconv_opt) {
|
||||
create_refuse_error(refused_no_iconv);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2074,18 +2066,30 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (orig_protect_args == 2 && am_server)
|
||||
protect_args = orig_protect_args;
|
||||
|
||||
if (protect_args == 1 && am_server)
|
||||
if (protect_args == 1 && am_server) {
|
||||
poptFreeContext(pc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*argv_p = argv = poptGetArgs(pc);
|
||||
*argc_p = argc = count_args(argv);
|
||||
/* Because popt 1.19 has started to free the returned args data, we now
|
||||
* make a copy of the array and then do an immediate cleanup. */
|
||||
argv = poptGetArgs(pc);
|
||||
argc = count_args(argv);
|
||||
if (!argc) {
|
||||
*argv_p = empty_argv;
|
||||
*argc_p = 0;
|
||||
} else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
|
||||
out_of_memory("parse_arguments");
|
||||
argv = *argv_p;
|
||||
poptFreeContext(pc);
|
||||
pc = NULL;
|
||||
|
||||
#ifndef SUPPORT_LINKS
|
||||
if (preserve_links && !am_sender) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"symlinks are not supported on this %s\n",
|
||||
am_server ? "server" : "client");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2094,7 +2098,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"hard links are not supported on this %s\n",
|
||||
am_server ? "server" : "client");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2102,20 +2106,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (am_root < 0 && preserve_xattrs > 1) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--fake-super conflicts with -XX\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
#else
|
||||
if (am_root < 0) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--fake-super requires an rsync with extended attributes enabled\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (write_batch && read_batch) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--write-batch and --read-batch can not be used together\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (write_batch > 0 || read_batch) {
|
||||
if (am_server) {
|
||||
@@ -2134,25 +2138,25 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (read_batch && files_from) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--read-batch cannot be used with --files-from\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (read_batch && remove_source_files) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--read-batch cannot be used with --remove-%s-files\n",
|
||||
remove_source_files == 1 ? "source" : "sent");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"the batch-file name must be %d characters or less.\n",
|
||||
MAX_BATCH_NAME_LEN);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"the --temp-dir path is WAY too long.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (max_delete < 0 && max_delete != INT_MIN) {
|
||||
@@ -2186,7 +2190,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (delete_before + !!delete_during + delete_after > 1) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"You may not combine multiple --delete-WHEN options.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (delete_before || delete_during || delete_after)
|
||||
delete_mode = 1;
|
||||
@@ -2197,7 +2201,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
delete_during = 1;
|
||||
else {
|
||||
create_refuse_error(refused_delete_before);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (refused_delete_during)
|
||||
delete_before = 1;
|
||||
@@ -2206,14 +2210,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (!xfer_dirs && delete_mode) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--delete does not work without --recursive (-r) or --dirs (-d).\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (missing_args == 3) /* simplify if both options were specified */
|
||||
missing_args = 2;
|
||||
if (refused_delete && (delete_mode || missing_args == 2)) {
|
||||
create_refuse_error(refused_delete);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (remove_source_files) {
|
||||
@@ -2222,7 +2226,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
* options. */
|
||||
if (refused_delete && am_sender) {
|
||||
create_refuse_error(refused_delete);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
need_messages_from_generator = 1;
|
||||
}
|
||||
@@ -2276,7 +2280,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--suffix cannot contain slashes: %s\n",
|
||||
backup_suffix);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (backup_dir) {
|
||||
size_t len;
|
||||
@@ -2289,7 +2293,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (len > sizeof backup_dir_buf - 128) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"the --backup-dir path is WAY too long.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
backup_dir_len = (int)len;
|
||||
if (!backup_dir_len) {
|
||||
@@ -2308,7 +2312,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
"--suffix cannot be empty %s\n", backup_dir_len < 0
|
||||
? "when --backup-dir is the same as the dest dir"
|
||||
: "without a --backup-dir");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
} else if (make_backups && delete_mode && !delete_excluded && !am_server) {
|
||||
snprintf(backup_dir_buf, sizeof backup_dir_buf,
|
||||
"P *%s", backup_suffix);
|
||||
@@ -2336,7 +2340,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (do_progress && !am_server) {
|
||||
if (!log_before_transfer && INFO_EQ(NAME, 0))
|
||||
parse_output_words(info_words, info_levels, "name", DEFAULT_PRIORITY);
|
||||
parse_output_words(info_words, info_levels, "flist2,progress", DEFAULT_PRIORITY);
|
||||
parse_output_words(info_words, info_levels, "FLIST2,PROGRESS", DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
if (dry_run)
|
||||
@@ -2377,11 +2381,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (whole_file > 0) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"--append cannot be used with --whole-file\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (refused_inplace) {
|
||||
create_refuse_error(refused_inplace);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
inplace = 1;
|
||||
}
|
||||
@@ -2389,7 +2393,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (write_devices) {
|
||||
if (refused_inplace) {
|
||||
create_refuse_error(refused_inplace);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
inplace = 1;
|
||||
}
|
||||
@@ -2404,13 +2408,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
"--%s cannot be used with --%s\n",
|
||||
append_mode ? "append" : "inplace",
|
||||
delay_updates ? "delay-updates" : "partial-dir");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
/* --inplace implies --partial for refusal purposes, but we
|
||||
* clear the keep_partial flag for internal logic purposes. */
|
||||
if (refused_partial) {
|
||||
create_refuse_error(refused_partial);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
keep_partial = 0;
|
||||
#else
|
||||
@@ -2418,7 +2422,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
"--%s is not supported on this %s\n",
|
||||
append_mode ? "append" : "inplace",
|
||||
am_server ? "server" : "client");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
#endif
|
||||
} else {
|
||||
if (keep_partial && !partial_dir && !am_server) {
|
||||
@@ -2432,7 +2436,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
partial_dir = NULL;
|
||||
if (!partial_dir && refused_partial) {
|
||||
create_refuse_error(refused_partial);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
keep_partial = 1;
|
||||
}
|
||||
@@ -2453,14 +2457,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
if (am_server) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"The --files-from sent to the server cannot specify a host.\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
files_from = p;
|
||||
filesfrom_host = h;
|
||||
if (strcmp(files_from, "-") == 0) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"Invalid --files-from remote filename\n");
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
if (sanitize_paths)
|
||||
@@ -2479,7 +2483,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"failed to open files-from file %s: %s\n",
|
||||
files_from, strerror(errno));
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2496,6 +2500,9 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
options_rejected:
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"Your options have been rejected by the server.\n");
|
||||
cleanup:
|
||||
if (pc)
|
||||
poptFreeContext(pc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2511,7 +2518,7 @@ static char SPLIT_ARG_WHEN_OLD[1];
|
||||
**/
|
||||
char *safe_arg(const char *opt, const char *arg)
|
||||
{
|
||||
#define SHELL_CHARS "!#$&;|<>(){}\"' \t\\"
|
||||
#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;
|
||||
@@ -2523,7 +2530,7 @@ char *safe_arg(const char *opt, const char *arg)
|
||||
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 == '~'
|
||||
if (*arg == '~' && is_filename_arg && !am_sender && !trust_sender_args
|
||||
&& ((relative_paths && !strstr(arg, "/./"))
|
||||
|| !strchr(arg, '/'))) {
|
||||
extras++;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Summary: A fast, versatile, remote (and local) file-copying tool
|
||||
Name: rsync
|
||||
Version: 3.2.7
|
||||
%define fullversion %{version}
|
||||
Release: 1
|
||||
%define srcdir src
|
||||
Version: 3.3.0
|
||||
%define fullversion %{version}pre1
|
||||
Release: 0.1.pre1
|
||||
%define srcdir src-previews
|
||||
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
|
||||
* Thu Oct 20 2022 Wayne Davison <wayne@opencoder.net>
|
||||
Released 3.2.7.
|
||||
* Sat Apr 29 2023 Wayne Davison <wayne@opencoder.net>
|
||||
Released 3.3.0pre1.
|
||||
|
||||
* Fri Mar 21 2008 Wayne Davison <wayne@opencoder.net>
|
||||
Added installation of /etc/xinetd.d/rsync file and some commented-out
|
||||
|
||||
@@ -36,15 +36,9 @@ int poptDupArgv(int argc, const char **argv,
|
||||
dst += (argc + 1) * sizeof(*argv);
|
||||
|
||||
/*@-branchstate@*/
|
||||
{
|
||||
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;
|
||||
}
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv2[i] = dst;
|
||||
dst += strlcpy(dst, argv[i], nb) + 1;
|
||||
}
|
||||
/*@=branchstate@*/
|
||||
argv2[argc] = NULL;
|
||||
|
||||
106
receiver.c
106
receiver.c
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1996-2000 Andrew Tridgell
|
||||
* Copyright (C) 1996 Paul Mackerras
|
||||
* Copyright (C) 2003-2022 Wayne Davison
|
||||
* Copyright (C) 2003-2023 Wayne Davison
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -66,11 +66,9 @@ 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;
|
||||
@@ -215,12 +213,7 @@ 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.) */
|
||||
/* 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);
|
||||
fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
|
||||
|
||||
#if 0
|
||||
/* In most cases parent directories will already exist because their
|
||||
@@ -318,12 +311,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
data = NULL;
|
||||
i = recv_token(f_in, &data);
|
||||
if (i == 0)
|
||||
break;
|
||||
|
||||
while ((i = recv_token(f_in, &data)) != 0) {
|
||||
if (INFO_GTE(PROGRESS, 1))
|
||||
show_progress(offset, total_size);
|
||||
|
||||
@@ -331,10 +319,6 @@ 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));
|
||||
@@ -352,11 +336,6 @@ 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 +372,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
|
||||
if (fd != -1 && offset > 0) {
|
||||
if (sparse_files > 0) {
|
||||
if (sparse_end(fd, offset) != 0)
|
||||
if (sparse_end(fd, offset, updating_basis_or_equiv) != 0)
|
||||
goto report_write_error;
|
||||
} else if (flush_write_file(fd) < 0) {
|
||||
report_write_error:
|
||||
@@ -456,7 +435,7 @@ 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_at(partialptr, fname) < 0) {
|
||||
if (do_rename(partialptr, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"rename failed for %s (from %s)",
|
||||
full_fname(fname), partialptr);
|
||||
@@ -472,10 +451,7 @@ 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;
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
file = flist->files[ndx - flist->ndx_start];
|
||||
struct file_struct *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));
|
||||
@@ -575,8 +551,6 @@ 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. */
|
||||
@@ -612,8 +586,6 @@ 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);
|
||||
@@ -744,34 +716,28 @@ 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) {
|
||||
basedir = file->dirname;
|
||||
}
|
||||
fnamecmp = xname;
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
} else
|
||||
fnamecmp = xname;
|
||||
break;
|
||||
default:
|
||||
if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
|
||||
fnamecmp_type -= FNAMECMP_FUZZY + 1;
|
||||
if (file->dirname) {
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], file->dirname);
|
||||
basedir = fnamecmpbuf;
|
||||
} else {
|
||||
basedir = basis_dir[fnamecmp_type];
|
||||
}
|
||||
fnamecmp = xname;
|
||||
stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
|
||||
basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
|
||||
} else
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
|
||||
} else if (fnamecmp_type >= basis_dir_cnt) {
|
||||
rprintf(FERROR,
|
||||
"invalid basis_dir index: %d.\n",
|
||||
fnamecmp_type);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
} else {
|
||||
basedir = basis_dir[fnamecmp_type];
|
||||
fnamecmp = fname;
|
||||
}
|
||||
} else
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
break;
|
||||
}
|
||||
if (!fnamecmp || (daemon_filter_list.head
|
||||
@@ -794,31 +760,25 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
|
||||
if (fd1 == -1 && protocol_version < 29) {
|
||||
if (fnamecmp != fname) {
|
||||
fnamecmp = fname;
|
||||
fnamecmp_type = FNAMECMP_FNAME;
|
||||
fd1 = do_open_nofollow(fnamecmp, O_RDONLY);
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
}
|
||||
|
||||
if (fd1 == -1 && basis_dir[0]) {
|
||||
/* pre-29 allowed only one alternate basis */
|
||||
basedir = basis_dir[0];
|
||||
fnamecmp = fname;
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
|
||||
basis_dir[0], fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
fnamecmp_type = FNAMECMP_BASIS_DIR_LOW;
|
||||
fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
|
||||
fd1 = do_open(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));
|
||||
@@ -879,21 +839,11 @@ 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;
|
||||
/* 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);
|
||||
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? */
|
||||
if (use_secure_symlinks)
|
||||
fd2 = secure_relative_open(NULL, fname, O_WRONLY, 0600);
|
||||
else
|
||||
fd2 = do_open(fname, O_WRONLY, 0600);
|
||||
fd2 = do_open(fname, O_WRONLY, 0600);
|
||||
}
|
||||
#endif
|
||||
if (fd2 == -1) {
|
||||
@@ -945,7 +895,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_at(partialptr);
|
||||
do_unlink(partialptr);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
} else if (keep_partial && partialptr && (!one_inplace || delay_updates)) {
|
||||
@@ -954,7 +904,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_at(fnametmp);
|
||||
do_unlink(fnametmp);
|
||||
recv_ok = -1;
|
||||
} else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
|
||||
file, recv_ok, !partial_dir))
|
||||
@@ -965,7 +915,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
} else
|
||||
partialptr = NULL;
|
||||
} else if (!one_inplace)
|
||||
do_unlink_at(fnametmp);
|
||||
do_unlink(fnametmp);
|
||||
|
||||
cleanup_disable();
|
||||
|
||||
|
||||
@@ -1636,7 +1636,9 @@ expand it.
|
||||
0. `--crtimes`, `-N,`
|
||||
|
||||
This tells rsync to set the create times (newness) of the destination
|
||||
files to the same value as the source files.
|
||||
files to the same value as the source files. Your OS & filesystem must
|
||||
support the setting of arbitrary creation (birth) times for this option
|
||||
to be supported.
|
||||
|
||||
0. `--omit-dir-times`, `-O`
|
||||
|
||||
|
||||
13
rsync.c
13
rsync.c
@@ -437,10 +437,7 @@ 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);
|
||||
free(s->sum2_array);
|
||||
}
|
||||
if (s->sums) free(s->sums);
|
||||
free(s);
|
||||
}
|
||||
|
||||
@@ -547,7 +544,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_at(fname, uid, gid) != 0) {
|
||||
if (do_lchown(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",
|
||||
@@ -657,7 +654,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_at(fname, new_mode);
|
||||
int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
|
||||
if (ret < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to set permissions on %s",
|
||||
@@ -758,7 +755,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_at(fnametmp);
|
||||
do_unlink(fnametmp);
|
||||
return 0;
|
||||
}
|
||||
if (ret == 0) {
|
||||
@@ -774,7 +771,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_at(fnametmp, fname) < 0) {
|
||||
if (do_rename(fnametmp, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\"",
|
||||
full_fname(fnametmp), fname);
|
||||
return 0;
|
||||
|
||||
28
rsync.h
28
rsync.h
@@ -84,7 +84,6 @@
|
||||
#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 */
|
||||
@@ -163,29 +162,6 @@
|
||||
/* 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 */
|
||||
@@ -982,12 +958,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 */
|
||||
@@ -1006,8 +982,6 @@ 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 */
|
||||
|
||||
@@ -74,25 +74,7 @@ reread the `rsyncd.conf` file. The file is re-read on each client connection.
|
||||
## GLOBAL PARAMETERS
|
||||
|
||||
The first parameters in the file (before a [module] header) are the global
|
||||
parameters. Rsync also allows for the use of a "[global]" module name to
|
||||
indicate the start of one or more global-parameter sections (the name must be
|
||||
lower case).
|
||||
|
||||
You may also include any module parameters in the global part of the config
|
||||
file in which case the supplied value will override the default for that
|
||||
parameter.
|
||||
|
||||
You may use references to environment variables in the values of parameters.
|
||||
String parameters will have %VAR% references expanded as late as possible (when
|
||||
the string is first used in the program), allowing for the use of variables
|
||||
that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
|
||||
parameters (such as true/false settings) are expanded when read from the config
|
||||
file. If a variable does not exist in the environment, or if a sequence of
|
||||
characters is not a valid reference (such as an un-paired percent sign), the
|
||||
raw characters are passed through unchanged. This helps with backward
|
||||
compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
|
||||
string in a path could result in a very unsafe path). The safest way to insert
|
||||
a literal % into a value is to use %%.
|
||||
parameters:
|
||||
|
||||
[comment]: # (An OL starting at 0 is converted into a DL by the parser.)
|
||||
|
||||
@@ -138,6 +120,22 @@ 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.
|
||||
|
||||
You may also include any [MODULE PARAMETERS](#) in the global part of the
|
||||
config file, in which case the supplied value will override the default for
|
||||
that parameter.
|
||||
|
||||
You may use references to environment variables in the values of parameters.
|
||||
String parameters will have %VAR% references expanded as late as possible (when
|
||||
the string is first used in the program), allowing for the use of variables
|
||||
that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
|
||||
parameters (such as true/false settings) are expanded when read from the config
|
||||
file. If a variable does not exist in the environment, or if a sequence of
|
||||
characters is not a valid reference (such as an un-paired percent sign), the
|
||||
raw characters are passed through unchanged. This helps with backward
|
||||
compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
|
||||
string in a path could result in a very unsafe path). The safest way to insert
|
||||
a literal % into a value is to use %%.
|
||||
|
||||
## MODULE PARAMETERS
|
||||
|
||||
After the global parameters you should define a number of modules, each module
|
||||
@@ -146,11 +144,17 @@ a module name in square brackets [module] followed by the parameters for that
|
||||
module. The module name cannot contain a slash or a closing square bracket.
|
||||
If the name contains whitespace, each internal sequence of whitespace will be
|
||||
changed into a single space, while leading or trailing whitespace will be
|
||||
discarded. Also, the name cannot be "global" as that exact name indicates that
|
||||
global parameters follow (see above).
|
||||
discarded.
|
||||
|
||||
As with GLOBAL PARAMETERS, you may use references to environment variables in
|
||||
the values of parameters. See the GLOBAL PARAMETERS section for more details.
|
||||
There is also a special module name of "[global]" that does not define a module
|
||||
but instead switches back to the global settings context where default
|
||||
parameters can be specified. Because each defined module gets its full set of
|
||||
parameters as a combination of the default values that are set at that position
|
||||
in the config file plus its own parameter list, the use of a "[global]" section
|
||||
can help to maintain shared config values for multiple modules.
|
||||
|
||||
As with [GLOBAL PARAMETERS](#), you may use references to environment variables
|
||||
in the values of parameters. See that section for details.
|
||||
|
||||
0. `comment`
|
||||
|
||||
|
||||
30
sender.c
30
sender.c
@@ -31,7 +31,6 @@ 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;
|
||||
@@ -48,8 +47,6 @@ 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;
|
||||
@@ -97,11 +94,10 @@ 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, sum2_at(s, i), s->s2length);
|
||||
read_buf(f, s->sums[i].sum2, s->s2length);
|
||||
|
||||
s->sums[i].offset = offset;
|
||||
s->sums[i].flags = 0;
|
||||
@@ -140,8 +136,6 @@ 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;
|
||||
@@ -266,8 +260,6 @@ 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)) {
|
||||
@@ -356,25 +348,7 @@ void send_files(int f_in, int f_out)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
fd = do_open(fname, O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
enum logcode c = am_daemon && protocol_version < 28 ? FERROR : FWARNING;
|
||||
|
||||
@@ -347,7 +347,8 @@ __attribute__ ((target("avx2"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf
|
||||
__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);
|
||||
__m256i mul_one;
|
||||
mul_one = _mm256_abs_epi8(_mm256_cmpeq_epi16(mul_one,mul_one)); // set all vector elements to 1
|
||||
|
||||
for (; i < (len-64); i+=64) {
|
||||
// Load ... 4*[int8*16]
|
||||
@@ -547,118 +548,6 @@ int main() {
|
||||
#pragma clang optimize on
|
||||
#endif /* BENCHMARK_SIMD_CHECKSUM1 */
|
||||
|
||||
#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,23 +47,21 @@ 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[PROXY_BUF_SIZE + 1];
|
||||
char *authhdr, authbuf[PROXY_BUF_SIZE + 1];
|
||||
char *cp, buffer[1024];
|
||||
char *authhdr, authbuf[1024];
|
||||
int len;
|
||||
|
||||
if (proxy_user && proxy_pass) {
|
||||
stringjoin(buffer, PROXY_BUF_SIZE,
|
||||
stringjoin(buffer, sizeof buffer,
|
||||
proxy_user, ":", proxy_pass, NULL);
|
||||
len = strlen(buffer);
|
||||
|
||||
if ((len*8 + 5) / 6 >= PROXY_BUF_SIZE - 3) {
|
||||
if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
|
||||
rprintf(FERROR,
|
||||
"authentication information is too long\n");
|
||||
return -1;
|
||||
@@ -76,14 +74,14 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
|
||||
authhdr = "";
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
if (write(fd, buffer, len) != len) {
|
||||
rsyserr(FERROR, errno, "failed to write to proxy");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE - 1]; cp++) {
|
||||
for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
|
||||
if (read(fd, cp, 1) != 1) {
|
||||
rsyserr(FERROR, errno, "failed to read from proxy");
|
||||
return -1;
|
||||
@@ -92,13 +90,11 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
|
||||
break;
|
||||
}
|
||||
|
||||
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 (*cp != '\n')
|
||||
cp++;
|
||||
*cp-- = '\0';
|
||||
if (*cp == '\r')
|
||||
*cp = '\0';
|
||||
if (strncmp(buffer, "HTTP/", 5) != 0) {
|
||||
rprintf(FERROR, "bad response from proxy -- %s\n",
|
||||
buffer);
|
||||
@@ -114,7 +110,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[PROXY_BUF_SIZE]; cp++) {
|
||||
for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
|
||||
if (read(fd, cp, 1) != 1) {
|
||||
rsyserr(FERROR, errno,
|
||||
"failed to read from proxy");
|
||||
|
||||
@@ -1,42 +1,63 @@
|
||||
#!/usr/bin/env perl
|
||||
#
|
||||
#!/usr/bin/env python3
|
||||
# This script finds all CVS/Entries files in the current directory and below
|
||||
# and creates a local .cvsinclude file with non-inherited rules including each
|
||||
# checked-in file. Then, use this option whenever using --cvs-exclude (-C):
|
||||
#
|
||||
# -f ': .cvsinclude'
|
||||
# -f ': .cvsinclude'
|
||||
#
|
||||
# That ensures that all checked-in files/dirs are included in the transfer.
|
||||
# (You could alternately put ": .cvsinclude" into an .rsync-filter file and
|
||||
# use the -F option, which is easier to type.)
|
||||
#
|
||||
# The downside is that you need to remember to re-run cvs2includes whenever
|
||||
# you add a new file to the project.
|
||||
use strict;
|
||||
# CVS gets an added or removed file. Maybe just run it before every copy.
|
||||
|
||||
open(FIND, 'find . -name CVS -type d |') or die $!;
|
||||
while (<FIND>) {
|
||||
chomp;
|
||||
s#^\./##;
|
||||
import os, argparse
|
||||
|
||||
my $entries = "$_/Entries";
|
||||
s/CVS$/.cvsinclude/;
|
||||
my $filter = $_;
|
||||
INC_NAME = '.cvsinclude'
|
||||
|
||||
open(ENTRIES, $entries) or die "Unable to open $entries: $!\n";
|
||||
my @includes;
|
||||
while (<ENTRIES>) {
|
||||
push(@includes, $1) if m#/(.+?)/#;
|
||||
}
|
||||
close ENTRIES;
|
||||
if (@includes) {
|
||||
open(FILTER, ">$filter") or die "Unable to write $filter: $!\n";
|
||||
print FILTER map "+ /$_\n", @includes;
|
||||
close FILTER;
|
||||
print "Updated $filter\n";
|
||||
} elsif (-f $filter) {
|
||||
unlink($filter);
|
||||
print "Removed $filter\n";
|
||||
}
|
||||
}
|
||||
close FIND;
|
||||
def main():
|
||||
if args.dir:
|
||||
os.chdir(args.dir)
|
||||
|
||||
cvs_includes = set()
|
||||
for root, dirs, files in os.walk('.'):
|
||||
if INC_NAME in files:
|
||||
cvs_includes.add((root + '/' + INC_NAME)[2:])
|
||||
if root.endswith('/CVS') and 'Entries' in files:
|
||||
entries = root[2:] + '/Entries'
|
||||
includes = [ ]
|
||||
with open(entries) as fh:
|
||||
for line in fh:
|
||||
if line.startswith(('/', 'D/')):
|
||||
includes.append(line.split('/', 2)[1])
|
||||
if includes:
|
||||
inc = root[2:-3] + INC_NAME
|
||||
cvs_includes.discard(inc)
|
||||
try:
|
||||
with open(inc) as fh:
|
||||
old_txt = fh.read()
|
||||
except OSError:
|
||||
old_txt = ''
|
||||
txt = ''.join(f"+ /{x}\n" for x in includes)
|
||||
if txt == old_txt:
|
||||
print("Unchanged", inc)
|
||||
else:
|
||||
print("Updating", inc)
|
||||
with open(inc, 'w') as fh:
|
||||
fh.write(txt)
|
||||
dirs.sort()
|
||||
|
||||
for inc in sorted(cvs_includes):
|
||||
print("Removing", inc)
|
||||
os.unlink(inc)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=f"Transform CVS/Entries into {INC_NAME} files.", add_help=False)
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
parser.add_argument("dir", nargs='?', help="The top CVS dir. Defaults to the current directory.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et
|
||||
|
||||
@@ -1,27 +1,37 @@
|
||||
#!/usr/bin/env perl
|
||||
# This script takes an input of filenames and outputs a set of
|
||||
# include/exclude directives that can be used by rsync to copy
|
||||
# just the indicated files using an --exclude-from=FILE option.
|
||||
use strict;
|
||||
#!/usr/bin/env python3
|
||||
# This script takes an input of filenames and outputs a set of include/exclude
|
||||
# directives that can be used by rsync to copy just the indicated files using
|
||||
# an --exclude-from=FILE or -f'. FILE' option. To be able to delete files on
|
||||
# the receiving side, either use --delete-excluded or change the exclude (-)
|
||||
# rules to hide filter rules (H) that only affect the sending side.
|
||||
|
||||
my %hash;
|
||||
import os, fileinput, argparse
|
||||
|
||||
while (<>) {
|
||||
chomp;
|
||||
s#^/+##;
|
||||
my $path = '/';
|
||||
while (m#([^/]+/)/*#g) {
|
||||
$path .= $1;
|
||||
print "+ $path\n" unless $hash{$path}++;
|
||||
}
|
||||
if (m#([^/]+)$#) {
|
||||
print "+ $path$1\n";
|
||||
} else {
|
||||
delete $hash{$path};
|
||||
}
|
||||
}
|
||||
def main():
|
||||
paths = set()
|
||||
for line in fileinput.input(args.files):
|
||||
dirs = line.strip().lstrip('/').split('/')
|
||||
if not dirs:
|
||||
continue
|
||||
for j in range(1, len(dirs)):
|
||||
if dirs[j] == '':
|
||||
continue
|
||||
path = '/' + '/'.join(dirs[:j]) + '/'
|
||||
if path not in paths:
|
||||
print('+', path)
|
||||
paths.add(path)
|
||||
print('+', '/' + '/'.join(dirs))
|
||||
|
||||
foreach (sort keys %hash) {
|
||||
print "- $_*\n";
|
||||
}
|
||||
print "- /*\n";
|
||||
for path in sorted(paths):
|
||||
print('-', path + '*')
|
||||
print('-', '/*')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Transform a list of files into a set of include/exclude rules.", add_help=False)
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
parser.add_argument("files", metavar="FILE", default='-', nargs='*', help="The file(s) that hold the pathnames to translate. Defaults to stdin.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et
|
||||
|
||||
45
support/idmap
Executable file
45
support/idmap
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
# This helper script makes it easy to use a passwd or group file to map values
|
||||
# in a LOCAL transfer. For instance, if you mount a backup that does not have
|
||||
# the same passwd setup as the local machine, you can do a copy to/from the
|
||||
# backup area as follows and get the differing ID values mapped just like a
|
||||
# remote transfer to/from the backed-up machine would do:
|
||||
#
|
||||
# rsync -av --usermap=`idmap --to /mnt/backup/etc/passwd` \
|
||||
# --groupmap=`idmap --to /mnt/backup/etc/group` \
|
||||
# /some/src/ /mnt/backup/some/dest/
|
||||
#
|
||||
# rsync -av --usermap=`idmap --from /mnt/backup/etc/passwd` \
|
||||
# --groupmap=`idmap --from /mnt/backup/etc/group` \
|
||||
# /mnt/backup/some/src/ /some/dest/
|
||||
|
||||
import re, fileinput, argparse
|
||||
|
||||
NAME_ID_RE = re.compile(r'^(\w+):[^:]+:(\d+)')
|
||||
|
||||
def main():
|
||||
maps = [ ]
|
||||
for line in fileinput.input(args.files):
|
||||
m = NAME_ID_RE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
if args.to:
|
||||
pair = (m[1], m[2])
|
||||
else:
|
||||
pair = (m[2], m[1])
|
||||
maps.append(':'.join(pair))
|
||||
print(','.join(maps))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Output usermap or groupmap args for rsync.", add_help=False)
|
||||
action = parser.add_argument_group()
|
||||
action = parser.add_mutually_exclusive_group(required=True)
|
||||
action.add_argument("--from", action="store_true", help="Output the map for use on the sending side.")
|
||||
action.add_argument("--to", action="store_true", help="Output the map for use on the receiving side.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
parser.add_argument("files", metavar="FILE", default='-', nargs='*', help="The file(s) that hold the name & id pairs. Defaults to stdin.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys, argparse, subprocess, json
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
# This helper script makes it easy to use a passwd or group file to map
|
||||
# values in a LOCAL transfer. For instance, if you mount a backup that
|
||||
# does not have the same passwd setup as the local machine, you can do
|
||||
# a copy FROM the backup area as follows and get the differing ID values
|
||||
# mapped just like a remote transfer FROM the backed-up machine would do:
|
||||
#
|
||||
# rsync -av --usermap=`mapfrom /mnt/backup/etc/passwd` \
|
||||
# --groupmap=`mapfrom /mnt/backup/etc/group` \
|
||||
# /mnt/backup/some/src/ /some/dest/
|
||||
|
||||
while (<>) {
|
||||
push @_, "$2:$1" if /^(\w+):[^:]+:(\d+)/;
|
||||
}
|
||||
print join(',', @_), "\n";
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
# This helper script makes it easy to use a passwd or group file to map
|
||||
# values in a LOCAL transfer. For instance, if you mount a backup that
|
||||
# does not have the same passwd setup as the local machine, you can do
|
||||
# a copy TO the backup area as follows and get the differing ID values
|
||||
# mapped just like a remote transfer TO the backed-up machine would do:
|
||||
#
|
||||
# rsync -av --usermap=`mapto /mnt/backup/etc/passwd` \
|
||||
# --groupmap=`mapto /mnt/backup/etc/group` \
|
||||
# /some/src/ /mnt/backup/some/dest/
|
||||
|
||||
while (<>) {
|
||||
push @_, "$1:$2" if /^(\w+):[^:]+:(\d+)/;
|
||||
}
|
||||
print join(',', @_), "\n";
|
||||
@@ -258,6 +258,9 @@ def main():
|
||||
|
||||
if args.munge:
|
||||
rsync_opts.append('--munge-links')
|
||||
|
||||
if args.no_overwrite:
|
||||
rsync_opts.append('--ignore-existing')
|
||||
|
||||
if not rsync_args:
|
||||
rsync_args = [ '.' ]
|
||||
@@ -364,6 +367,7 @@ if __name__ == '__main__':
|
||||
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('-no-overwrite', action='store_true', help="Prevent overwriting existing files by enforcing --ignore-existing")
|
||||
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()
|
||||
|
||||
@@ -5,7 +5,7 @@ rrsync - a script to setup restricted rsync users via ssh logins
|
||||
## SYNOPSIS
|
||||
|
||||
```
|
||||
rrsync [-ro|-rw] [-munge] [-no-del] [-no-lock] DIR
|
||||
rrsync [-ro|-rw] [-munge] [-no-del] [-no-lock] [-no-overwrite] DIR
|
||||
```
|
||||
|
||||
The single non-option argument specifies the restricted _DIR_ to use. It can be
|
||||
@@ -85,6 +85,11 @@ The remainder of this manpage is dedicated to using the rrsync script.
|
||||
|
||||
Avoid the single-run (per-user) lock check. Useful with [`-munge`](#opt).
|
||||
|
||||
0. `-no-overwrite`
|
||||
|
||||
Enforce `--ignore-existing` on the server. Prevents overwriting existing
|
||||
files when the server is the receiver.
|
||||
|
||||
0. `-help`, `-h`
|
||||
|
||||
Output this help message and exit.
|
||||
|
||||
117
t_chmod_secure.c
117
t_chmod_secure.c
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
2
t_stub.c
2
t_stub.c
@@ -23,8 +23,6 @@
|
||||
|
||||
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;
|
||||
|
||||
@@ -28,9 +28,6 @@ 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,113 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,206 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,135 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/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,80 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/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 Finding 3a: copy_file()'s source
|
||||
# open in copy_altdest_file() is via do_open_nofollow(), which only
|
||||
# refuses a final-component symlink. Parent components are still
|
||||
# resolved with normal symlink-following. A daemon module attacker
|
||||
# who plants a parent symlink at module/cd -> /outside, then runs
|
||||
# --copy-dest=cd against a source file matching the size+mtime of
|
||||
# /outside/target.txt, drives the receiver to:
|
||||
#
|
||||
# 1. Find a match-level >= 2 basis at "cd/target.txt"
|
||||
# 2. Call copy_altdest_file -> copy_file(src="cd/target.txt", ...)
|
||||
# 3. do_open_nofollow follows the "cd" parent symlink and reads
|
||||
# the contents of /outside/target.txt under the daemon's
|
||||
# authority
|
||||
# 4. Copy that content into the module destination
|
||||
#
|
||||
# Result: outside/target.txt content lands at module/target.txt,
|
||||
# accessible to the attacker on a subsequent pull.
|
||||
#
|
||||
# We detect by content: src/target.txt and outside/target.txt have
|
||||
# identical metadata (size + mtime + mode) but different content.
|
||||
# After the transfer, module/target.txt should match src (no
|
||||
# basedir escape) -- if it matches outside, the bug copied across
|
||||
# the symlink boundary.
|
||||
|
||||
. "$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"
|
||||
|
||||
# Outside-the-module file the daemon should not read on the
|
||||
# attacker's behalf.
|
||||
echo "OUTSIDE_LEAKED_DATA!" > "$outside/target.txt"
|
||||
chmod 0644 "$outside/target.txt"
|
||||
|
||||
# The symlink trap.
|
||||
ln -s "$outside" "$mod/cd"
|
||||
|
||||
# Source: same size, same mtime, same mode as outside -- so the
|
||||
# generator's link_stat + quick_check_ok finds a match-level >= 2
|
||||
# basis and calls copy_altdest_file.
|
||||
echo "ATTACKER_KNOWN_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 and fail to mkstemp in the scratch dir; force it to
|
||||
# keep our uid/gid in that case.
|
||||
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
|
||||
|
||||
# --copy-dest push to module root.
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC -rtp --copy-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
|
||||
|
||||
if cmp -s "$mod/target.txt" "$outside/target.txt"; then
|
||||
test_fail "basedir-escape via copy_file source: module/target.txt now contains the contents of outside/target.txt -- daemon read /outside via the cd symlink and copied it into the module"
|
||||
fi
|
||||
|
||||
if ! cmp -s "$mod/target.txt" "$src/target.txt"; then
|
||||
test_fail "destination doesn't match source content (and isn't outside content either): unexpected state"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,111 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 by Andrew Tridgell
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Regression test for GHSA-rjfm-3w2m-jf4f: a hostname-based "hosts deny"
|
||||
# rule must still match when the daemon performs a 'daemon chroot' and
|
||||
# the chroot does not contain the NSS files glibc needs for reverse DNS.
|
||||
#
|
||||
# Pre-fix, reverse DNS happened *after* the daemon chroot. With an empty
|
||||
# chroot the NSS lookup failed, client_name() returned "UNKNOWN", and a
|
||||
# deny rule referring to the connecting hostname silently failed to
|
||||
# match.
|
||||
#
|
||||
# Two scenarios are exercised so we can distinguish the case the fix
|
||||
# definitely covers from the per-module path that may still be
|
||||
# vulnerable:
|
||||
# A. global "reverse lookup = yes" (covered by b6abdb4c)
|
||||
# B. only module "reverse lookup = yes" (gap to verify)
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
case `uname -s` in
|
||||
Linux*) ;;
|
||||
*) test_skipped "test is Linux-specific (uses chroot+unshare)" ;;
|
||||
esac
|
||||
|
||||
# We need CAP_SYS_CHROOT. Re-exec under a user namespace if not root.
|
||||
if ! chroot / /bin/true 2>/dev/null; then
|
||||
if [ -z "$RSYNC_UNSHARED" ] && unshare --user --map-root-user true 2>/dev/null; then
|
||||
echo "Re-running under unshare --user --map-root-user..."
|
||||
RSYNC_UNSHARED=1 exec unshare --user --map-root-user "$SHELL_PATH" $RUNSHFLAGS "$0"
|
||||
fi
|
||||
test_skipped "need CAP_SYS_CHROOT (root or unshare --user --map-root-user)"
|
||||
fi
|
||||
|
||||
# We need 127.0.0.1 to reverse-resolve to a real hostname while NSS is
|
||||
# still working (i.e. before the daemon's chroot). The daemon will
|
||||
# look that name up itself as part of its hostname-based ACL check;
|
||||
# we then deny that name and assert the connection is rejected.
|
||||
client_hostname=`getent hosts 127.0.0.1 2>/dev/null | awk 'NR==1 {print $2}'`
|
||||
if [ -z "$client_hostname" ] || [ "$client_hostname" = "127.0.0.1" ]; then
|
||||
test_skipped "no reverse DNS for 127.0.0.1"
|
||||
fi
|
||||
|
||||
chrootdir="$scratchdir/chroot"
|
||||
rm -rf "$chrootdir"
|
||||
mkdir -p "$chrootdir/modroot"
|
||||
echo "from chroot" > "$chrootdir/modroot/file1"
|
||||
|
||||
conf="$scratchdir/test-rsyncd.conf"
|
||||
logfile="$scratchdir/rsyncd.log"
|
||||
|
||||
write_conf() {
|
||||
cat >"$conf" <<EOF
|
||||
use chroot = no
|
||||
log file = $logfile
|
||||
daemon chroot = $chrootdir
|
||||
reverse lookup = $1
|
||||
hosts deny = $client_hostname
|
||||
max verbosity = 4
|
||||
|
||||
[chrootmod]
|
||||
path = /modroot
|
||||
read only = yes
|
||||
reverse lookup = $2
|
||||
EOF
|
||||
}
|
||||
|
||||
# Run a transfer and return 0 if the daemon refused with @ERROR access
|
||||
# denied (the expected outcome when the deny rule matches).
|
||||
run_check() {
|
||||
label="$1"
|
||||
|
||||
rm -f "$logfile"
|
||||
rm -rf "$todir"
|
||||
mkdir -p "$todir"
|
||||
|
||||
out="$scratchdir/run.out"
|
||||
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC -av localhost::chrootmod/ "$todir/" >"$out" 2>&1
|
||||
rc=$?
|
||||
|
||||
echo "----- $label (rsync exit $rc):"
|
||||
cat "$out"
|
||||
echo "----- daemon log:"
|
||||
[ -f "$logfile" ] && cat "$logfile"
|
||||
echo "-----"
|
||||
|
||||
grep -q '@ERROR.*access denied' "$out"
|
||||
}
|
||||
|
||||
# Scenario A: global reverse lookup. Covered by b6abdb4c.
|
||||
write_conf yes yes
|
||||
if ! run_check "Scenario A (global reverse lookup = yes)"; then
|
||||
test_fail "Scenario A: hostname deny rule was bypassed"
|
||||
fi
|
||||
|
||||
# Scenario B: only the per-module reverse-lookup setting is enabled.
|
||||
# The b6abdb4c fix only pre-warms client_name()'s cache when the
|
||||
# global setting is on, so the post-chroot lookup in this path may
|
||||
# still produce "UNKNOWN" and bypass the deny rule.
|
||||
write_conf no yes
|
||||
if ! run_check "Scenario B (per-module reverse lookup only)"; then
|
||||
test_fail "Scenario B: hostname deny rule was bypassed (per-module reverse lookup with daemon chroot still has the bypass)"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2026 by Andrew Tridgell
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Test that a daemon module configured with "refuse options = compress"
|
||||
# rejects clients that ask for compression and still serves the same
|
||||
# transfer when the client does not.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
build_rsyncd_conf
|
||||
|
||||
# Append a module that refuses --compress (-z).
|
||||
cat >>"$conf" <<EOF
|
||||
|
||||
[no-compress]
|
||||
path = $fromdir
|
||||
read only = yes
|
||||
refuse options = compress
|
||||
EOF
|
||||
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon"
|
||||
export RSYNC_CONNECT_PROG
|
||||
|
||||
hands_setup
|
||||
|
||||
# Build a reference tree mirroring the daemon's global exclude rule.
|
||||
$RSYNC -av --exclude=foobar.baz "$fromdir/" "$chkdir/"
|
||||
|
||||
# A compressed transfer must be refused.
|
||||
errlog="$scratchdir/refuse.err"
|
||||
if $RSYNC -avz localhost::no-compress/ "$todir/" >/dev/null 2>"$errlog"; then
|
||||
cat "$errlog" >&2
|
||||
test_fail "compressed transfer was not refused"
|
||||
fi
|
||||
|
||||
grep -- '--compress' "$errlog" >/dev/null || {
|
||||
cat "$errlog" >&2
|
||||
test_fail "expected refuse error mentioning --compress"
|
||||
}
|
||||
|
||||
# The same transfer without -z must succeed.
|
||||
rm -rf "$todir"
|
||||
mkdir "$todir"
|
||||
checkit "$RSYNC -av localhost::no-compress/ '$todir/'" "$chkdir" "$todir"
|
||||
|
||||
# The script would have aborted on error, so getting here means we've won.
|
||||
exit 0
|
||||
@@ -1,128 +0,0 @@
|
||||
#!/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 off-by-one stack OOB write in
|
||||
# establish_proxy_connection() in socket.c when a malicious or
|
||||
# man-in-the-middle HTTP proxy returns a first response line of
|
||||
# 1023+ bytes without a '\n' terminator.
|
||||
#
|
||||
# Pre-fix: the read loop walked buffer[0..sizeof-2] one byte at a
|
||||
# time, then post-loop logic did "if (*cp != '\n') cp++; *cp-- =
|
||||
# '\0';". If no newline arrived before the loop filled the buffer,
|
||||
# cp was left at &buffer[sizeof-1] (never written by the loop),
|
||||
# *cp held stale stack bytes, and cp++ pushed cp one past the array.
|
||||
# The null-termination then wrote one byte out of bounds on the
|
||||
# stack. AddressSanitizer reports stack-buffer-overflow at the
|
||||
# null-termination site.
|
||||
#
|
||||
# Post-fix: the bound-exhaustion case is detected by position and
|
||||
# rejected with an "proxy response line too long" message, so no
|
||||
# OOB write occurs and rsync exits with a non-signal status.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
command -v python3 >/dev/null 2>&1 || test_skipped "python3 not available"
|
||||
|
||||
workdir="$scratchdir/workdir"
|
||||
mkdir -p "$workdir"
|
||||
cd "$workdir"
|
||||
|
||||
port_file="$workdir/port"
|
||||
proxy_log="$workdir/proxy.log"
|
||||
|
||||
# A minimal TCP listener: binds to an ephemeral port on 127.0.0.1,
|
||||
# writes the chosen port to $port_file *before* accept() so the test
|
||||
# can synchronise without a sleep, accepts one connection, reads
|
||||
# until end-of-headers or 64 KiB, sends exactly 1023 bytes of 'X'
|
||||
# with no '\n', then closes.
|
||||
python3 - "$port_file" >"$proxy_log" 2>&1 <<'PYEOF' &
|
||||
import socket, sys, os
|
||||
port_file = sys.argv[1]
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(("127.0.0.1", 0))
|
||||
port = s.getsockname()[1]
|
||||
tmp = port_file + ".tmp"
|
||||
with open(tmp, "w") as fp:
|
||||
fp.write("%d\n" % port)
|
||||
os.rename(tmp, port_file) # atomic visibility to the shell side
|
||||
s.listen(1)
|
||||
conn, _ = s.accept()
|
||||
conn.settimeout(5)
|
||||
try:
|
||||
data = b""
|
||||
while b"\r\n\r\n" not in data and len(data) < 65536:
|
||||
chunk = conn.recv(8192)
|
||||
if not chunk:
|
||||
break
|
||||
data += chunk
|
||||
except socket.timeout:
|
||||
pass
|
||||
conn.sendall(b"X" * 1023) # exactly the buffer-1 trigger size
|
||||
try:
|
||||
conn.shutdown(socket.SHUT_RDWR)
|
||||
except OSError:
|
||||
pass
|
||||
conn.close()
|
||||
s.close()
|
||||
PYEOF
|
||||
proxy_pid=$!
|
||||
|
||||
# Wait up to ~10s for the listener to publish its port.
|
||||
i=0
|
||||
while [ ! -s "$port_file" ] && [ $i -lt 10 ]; do
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
if [ ! -s "$port_file" ]; then
|
||||
kill "$proxy_pid" 2>/dev/null
|
||||
cat "$proxy_log" >&2 2>/dev/null
|
||||
test_fail "proxy listener never published a port"
|
||||
fi
|
||||
|
||||
port=`cat "$port_file"`
|
||||
case "$port" in
|
||||
*[!0-9]*|"") kill "$proxy_pid" 2>/dev/null; test_fail "bogus port from listener: '$port'" ;;
|
||||
esac
|
||||
|
||||
# Run rsync through the malicious proxy. Any rsync:// URL works:
|
||||
# the proxy intercepts the CONNECT and never forwards anywhere.
|
||||
rsync_err="$workdir/rsync.err"
|
||||
|
||||
# rsync MUST exit non-zero here (the proxy is misbehaving).
|
||||
# Use `|| status=$?` so we capture the real exit code under `sh -e`;
|
||||
# `if ! cmd; then status=$?` would only ever see 0 because the `!`
|
||||
# is the last command before `$?`.
|
||||
status=0
|
||||
RSYNC_PROXY="127.0.0.1:$port" \
|
||||
$RSYNC rsync://example.invalid:873/whatever/ "$workdir/out/" \
|
||||
>/dev/null 2>"$rsync_err" || status=$?
|
||||
|
||||
# Reap the listener.
|
||||
wait "$proxy_pid" 2>/dev/null || true
|
||||
|
||||
# 1. rsync must not have crashed (SIGSEGV/SIGABRT report >= 128).
|
||||
if [ "$status" -ge 128 ]; then
|
||||
cat "$rsync_err" >&2
|
||||
test_fail "rsync killed by signal (status=$status) -- possible stack OOB regression"
|
||||
fi
|
||||
|
||||
# 2. rsync must have actually exited non-zero (i.e. saw the bad proxy).
|
||||
if [ "$status" -eq 0 ]; then
|
||||
cat "$rsync_err" >&2
|
||||
test_fail "rsync returned success despite malformed proxy response"
|
||||
fi
|
||||
|
||||
# 3. The new error message must appear.
|
||||
if ! grep -q "proxy response line too long" "$rsync_err"; then
|
||||
cat "$rsync_err" >&2
|
||||
test_fail "expected 'proxy response line too long' in rsync stderr"
|
||||
fi
|
||||
|
||||
echo "OK: over-long proxy response line rejected cleanly without crashing"
|
||||
exit 0
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
test_symlink() {
|
||||
is_a_link "$1" || test_fail "File $1 is not a symlink"
|
||||
}
|
||||
|
||||
test_regular() {
|
||||
if [ ! -f "$1" ]; then
|
||||
test_fail "File $1 is not regular file or not exists"
|
||||
fi
|
||||
}
|
||||
|
||||
test_notexist() {
|
||||
if [ -e "$1" ]; then
|
||||
test_fail "File $1 exists"
|
||||
fi
|
||||
if [ -h "$1" ]; then
|
||||
test_fail "File $1 exists as a symlink"
|
||||
fi
|
||||
}
|
||||
|
||||
cd "$tmpdir"
|
||||
|
||||
mkdir from
|
||||
|
||||
mkdir "from/safe"
|
||||
mkdir "from/unsafe"
|
||||
|
||||
mkdir "from/safe/files"
|
||||
mkdir "from/safe/links"
|
||||
|
||||
touch "from/safe/files/file1"
|
||||
touch "from/safe/files/file2"
|
||||
touch "from/unsafe/unsafefile"
|
||||
|
||||
ln -s ../files/file1 "from/safe/links/"
|
||||
ln -s ../files/file2 "from/safe/links/"
|
||||
ln -s ../../unsafe/unsafefile "from/safe/links/"
|
||||
ln -s a/a/a/../../../unsafe2 "from/safe/links/"
|
||||
|
||||
#echo "LISTING FROM"
|
||||
#ls -lR from
|
||||
|
||||
echo "rsync with relative path and just -a"
|
||||
$RSYNC -avv --safe-links from/safe/ to
|
||||
|
||||
#echo "LISTING TO"
|
||||
#ls -lR to
|
||||
|
||||
test_symlink to/links/file1
|
||||
test_symlink to/links/file2
|
||||
test_notexist to/links/unsafefile
|
||||
test_notexist to/links/unsafe2
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/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 Finding 5: secure_relative_open()'s
|
||||
# front-door input check rejects "../foo" and "foo/../bar" but
|
||||
# misses bare "..", "subdir/..", and other variants whose "/"-split
|
||||
# components contain a literal "..". The kernel-enforced
|
||||
# RESOLVE_BENEATH (Linux 5.6+) and O_RESOLVE_BENEATH
|
||||
# (FreeBSD 13+, macOS 15+) reject these in-kernel; the per-component
|
||||
# walk fallback used on NetBSD, OpenBSD, Solaris, Cygwin and pre-5.6
|
||||
# Linux does not -- so the validation must happen at the front door.
|
||||
#
|
||||
# This test invokes the t_secure_relpath helper, which calls
|
||||
# secure_relative_open() with each suspect input and verifies the
|
||||
# return value is -1 with errno == EINVAL. EINVAL is the marker
|
||||
# that the front-door rejected the input, not the kernel; pre-fix
|
||||
# the kernel returns -1 with EXDEV (or, on the per-component
|
||||
# fallback, may return a valid fd at all -- "escape").
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
testdir="$scratchdir/relpath-test"
|
||||
rm -rf "$testdir"
|
||||
mkdir -p "$testdir"
|
||||
|
||||
if ! "$TOOLDIR/t_secure_relpath" "$testdir"; then
|
||||
test_fail "t_secure_relpath rejected one or more inputs incorrectly (see stderr above for the specific case)"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/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 re-check finding: the sender-side file-
|
||||
# list generator can still follow an attacker-planted symlink out of
|
||||
# the module via change_pathname() -> change_dir(...,CD_SKIP_CHDIR)
|
||||
# followed by change_dir(...,CD_NORMAL). The CD_SKIP_CHDIR sets
|
||||
# skipped_chdir=1, and the next CD_NORMAL call's secure-branch in
|
||||
# util1.c is gated on `!skipped_chdir`, so the secure path is
|
||||
# bypassed and a raw chdir(curr_dir) follows attacker-controlled
|
||||
# symlinks during flist generation.
|
||||
#
|
||||
# Reach: rsync daemon module with `use chroot = no`. A local
|
||||
# attacker plants module/cd -> /outside. A client (innocent or
|
||||
# malicious) pulls rsync://<daemon>/<module>/cd/. The daemon, as
|
||||
# sender, enumerates files in /outside and ships their metadata
|
||||
# (names, sizes, modes, mtimes) to the client. The actual content
|
||||
# transfer fails later at the secure_relative_open step with EXDEV,
|
||||
# but by then the metadata has already leaked.
|
||||
#
|
||||
# We detect by running a dry-run pull of the symlinked subdir and
|
||||
# checking whether the client's --list-only output mentions any
|
||||
# file from /outside. With the bug, /outside/secret.txt appears in
|
||||
# the list with its size; with the fix, the daemon's chdir into
|
||||
# the symlinked subdir is rejected and no /outside file is listed.
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
case "$(uname -s)" in
|
||||
SunOS|OpenBSD|NetBSD|CYGWIN*)
|
||||
test_skipped "secure change_dir relies on RESOLVE_BENEATH-equivalent kernel support not available on $(uname -s)"
|
||||
;;
|
||||
esac
|
||||
|
||||
mod="$scratchdir/module"
|
||||
outside="$scratchdir/outside"
|
||||
listfile="$scratchdir/listed.txt"
|
||||
conf="$scratchdir/test-rsyncd.conf"
|
||||
|
||||
rm -rf "$mod" "$outside"
|
||||
mkdir -p "$mod" "$outside"
|
||||
|
||||
# Outside-the-module file the daemon should NOT enumerate to clients.
|
||||
# A distinctive name + non-trivial size makes the leak easy to spot.
|
||||
echo "OUTSIDE_PROTECTED_FILE_USED_AS_LEAK_DETECTOR" > "$outside/leak_marker.txt"
|
||||
chmod 0644 "$outside/leak_marker.txt"
|
||||
|
||||
# The symlink trap planted by the local attacker.
|
||||
ln -s "$outside" "$mod/cd"
|
||||
|
||||
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
|
||||
|
||||
# Pull recursively into the symlinked subdir with dry-run + verbose,
|
||||
# capturing the daemon's flist (file list) on stdout. If the daemon
|
||||
# enumerates /outside, leak_marker.txt will appear in the listing.
|
||||
RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" \
|
||||
$RSYNC -nrv rsync://localhost/upload/cd/ "$scratchdir/dst/" \
|
||||
> "$listfile" 2>&1 || true
|
||||
|
||||
if grep -q "leak_marker\.txt" "$listfile"; then
|
||||
echo "----- leaked listing follows" >&2
|
||||
sed 's/^/ /' "$listfile" >&2
|
||||
echo "----- leaked listing ends" >&2
|
||||
test_fail "sender flist leak: outside/leak_marker.txt was enumerated to the client (daemon's chdir followed the cd symlink during flist generation)"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Test SIMD checksum implementations against the C reference
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
if ! test -x "$TOOLDIR/simdtest"; then
|
||||
test_skipped "simdtest not built (SIMD not available)"
|
||||
fi
|
||||
|
||||
"$TOOLDIR/simdtest"
|
||||
@@ -1,259 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Test that updating a file through a directory symlink works when using
|
||||
# -K (--copy-dirlinks). This is a regression test for:
|
||||
# https://github.com/RsyncProject/rsync/issues/715
|
||||
#
|
||||
# The CVE fix in commit c35e283 introduced secure_relative_open() which
|
||||
# uses O_NOFOLLOW on all path components, breaking legitimate directory
|
||||
# symlinks on the receiver side. The fix splits the path into basedir
|
||||
# (dirname, symlinks followed) and basename (O_NOFOLLOW) so that
|
||||
# directory symlinks are traversed while the final file component is
|
||||
# still protected.
|
||||
#
|
||||
# The regression only manifests when delta matching is triggered (i.e.,
|
||||
# the sender finds matching blocks in the old file). Small files with
|
||||
# completely different content are transferred in full and don't trigger
|
||||
# the bug. We use a large file with a small modification to ensure
|
||||
# delta transfer is used.
|
||||
#
|
||||
# In addition to the original regression, this test covers edge cases
|
||||
# in the fix itself:
|
||||
# - --backup with directory symlinks (finish_transfer pointer identity)
|
||||
# - --partial-dir with protocol < 29 (fnamecmp != partialptr guard)
|
||||
# - --inplace with directory symlinks (updating_basis_or_equiv check)
|
||||
# - Files without a dirname (top-level files, no split needed)
|
||||
|
||||
. "$suitedir/rsync.fns"
|
||||
|
||||
# secure_relative_open() uses kernel-enforced "stay below dirfd" via
|
||||
# openat2(RESOLVE_BENEATH) on Linux 5.6+ and openat(O_RESOLVE_BENEATH)
|
||||
# on FreeBSD 13+. Other platforms fall back to a per-component
|
||||
# O_NOFOLLOW walk that rejects every symlink including legitimate
|
||||
# directory symlinks -- the very case this test exercises. Skip on
|
||||
# those rather than report a known failure.
|
||||
case "$(uname -s)" in
|
||||
SunOS|OpenBSD|NetBSD|CYGWIN*)
|
||||
test_skipped "secure_relative_open lacks RESOLVE_BENEATH equivalent on $(uname -s); issue #715 still affects this platform"
|
||||
;;
|
||||
esac
|
||||
|
||||
RSYNC_RSH="$scratchdir/src/support/lsh.sh"
|
||||
export RSYNC_RSH
|
||||
|
||||
# $HOME is set to $scratchdir by rsync.fns
|
||||
# localhost: destination will cd to $HOME (i.e., $scratchdir)
|
||||
|
||||
# Helper: create a large file suitable for delta transfers.
|
||||
# ~32KB is large enough for rsync's block matching to find matches.
|
||||
make_testfile() {
|
||||
dd if=/dev/urandom of="$1" bs=1024 count=32 2>/dev/null \
|
||||
|| test_fail "failed to create test file $1"
|
||||
}
|
||||
|
||||
# Set up source tree
|
||||
srcbase="$tmpdir/src"
|
||||
|
||||
######################################################################
|
||||
# Test 1: Basic directory symlink update (the original issue #715)
|
||||
######################################################################
|
||||
|
||||
mkdir -p "$HOME/real-dir"
|
||||
ln -s real-dir "$HOME/dir"
|
||||
|
||||
mkdir -p "$srcbase/dir"
|
||||
make_testfile "$srcbase/dir/file"
|
||||
|
||||
# First transfer (initial): should create the file through the symlink
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 1: initial transfer failed"
|
||||
|
||||
if [ ! -f "$HOME/real-dir/file" ]; then
|
||||
test_fail "test 1: initial transfer did not create file through symlink"
|
||||
fi
|
||||
|
||||
diff "$srcbase/dir/file" "$HOME/real-dir/file" >/dev/null \
|
||||
|| test_fail "test 1: initial transfer content mismatch"
|
||||
|
||||
# Small modification to trigger delta transfer
|
||||
echo "appended update" >> "$srcbase/dir/file"
|
||||
sleep 1
|
||||
touch "$srcbase/dir/file"
|
||||
|
||||
# Second transfer (update): was failing with "failed verification"
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 1: update through directory symlink failed"
|
||||
|
||||
diff "$srcbase/dir/file" "$HOME/real-dir/file" >/dev/null \
|
||||
|| test_fail "test 1: update transfer content mismatch"
|
||||
|
||||
######################################################################
|
||||
# Test 2: Compression (-z) as in the original reproducer
|
||||
######################################################################
|
||||
|
||||
echo "another line" >> "$srcbase/dir/file"
|
||||
sleep 1
|
||||
touch "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptzv --rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 2: compressed update through directory symlink failed"
|
||||
|
||||
diff "$srcbase/dir/file" "$HOME/real-dir/file" >/dev/null \
|
||||
|| test_fail "test 2: compressed update content mismatch"
|
||||
|
||||
######################################################################
|
||||
# Test 3: Nested directory symlinks (nested/sub/data.txt where
|
||||
# "nested" is a symlink to "nested_real")
|
||||
######################################################################
|
||||
|
||||
mkdir -p "$HOME/nested_real/sub"
|
||||
ln -s nested_real "$HOME/nested"
|
||||
|
||||
mkdir -p "$srcbase/nested/sub"
|
||||
make_testfile "$srcbase/nested/sub/data.txt"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --rsync-path="$RSYNC" nested/sub/data.txt localhost:) \
|
||||
|| test_fail "test 3: initial nested transfer failed"
|
||||
|
||||
echo "appended nested" >> "$srcbase/nested/sub/data.txt"
|
||||
sleep 1
|
||||
touch "$srcbase/nested/sub/data.txt"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --rsync-path="$RSYNC" nested/sub/data.txt localhost:) \
|
||||
|| test_fail "test 3: update through nested directory symlink failed"
|
||||
|
||||
diff "$srcbase/nested/sub/data.txt" "$HOME/nested_real/sub/data.txt" >/dev/null \
|
||||
|| test_fail "test 3: nested update content mismatch"
|
||||
|
||||
######################################################################
|
||||
# Test 4: --backup with directory symlinks
|
||||
#
|
||||
# Exercises the finish_transfer() "fnamecmp == fname" pointer
|
||||
# comparison that determines whether to update fnamecmp to the
|
||||
# backup name. If broken, --backup would reference a renamed file
|
||||
# for xattr handling.
|
||||
######################################################################
|
||||
|
||||
# Reset destination
|
||||
rm -f "$HOME/real-dir/file" "$HOME/real-dir/file~"
|
||||
|
||||
make_testfile "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 4: initial transfer for backup test failed"
|
||||
|
||||
echo "backup update" >> "$srcbase/dir/file"
|
||||
sleep 1
|
||||
touch "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --backup --rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 4: update with --backup through directory symlink failed"
|
||||
|
||||
diff "$srcbase/dir/file" "$HOME/real-dir/file" >/dev/null \
|
||||
|| test_fail "test 4: backup update content mismatch"
|
||||
|
||||
if [ ! -f "$HOME/real-dir/file~" ]; then
|
||||
test_fail "test 4: backup file was not created"
|
||||
fi
|
||||
|
||||
######################################################################
|
||||
# Test 5: --inplace with directory symlinks
|
||||
#
|
||||
# Exercises the updating_basis_or_equiv check which uses
|
||||
# "fnamecmp == fname". With --inplace, rsync writes directly to
|
||||
# the destination file instead of a temp file.
|
||||
######################################################################
|
||||
|
||||
rm -f "$HOME/real-dir/file" "$HOME/real-dir/file~"
|
||||
|
||||
make_testfile "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --inplace --rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 5: initial inplace transfer failed"
|
||||
|
||||
echo "inplace update" >> "$srcbase/dir/file"
|
||||
sleep 1
|
||||
touch "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --inplace --rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 5: inplace update through directory symlink failed"
|
||||
|
||||
diff "$srcbase/dir/file" "$HOME/real-dir/file" >/dev/null \
|
||||
|| test_fail "test 5: inplace update content mismatch"
|
||||
|
||||
######################################################################
|
||||
# Test 6: Top-level file (no dirname, no split needed)
|
||||
#
|
||||
# Ensures the dirname/basename split is not attempted for files
|
||||
# at the top level (file->dirname is NULL).
|
||||
######################################################################
|
||||
|
||||
make_testfile "$srcbase/topfile"
|
||||
mkdir -p "$HOME"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -Rlptv --rsync-path="$RSYNC" topfile localhost:) \
|
||||
|| test_fail "test 6: initial top-level transfer failed"
|
||||
|
||||
echo "toplevel update" >> "$srcbase/topfile"
|
||||
sleep 1
|
||||
touch "$srcbase/topfile"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -Rlptv --rsync-path="$RSYNC" topfile localhost:) \
|
||||
|| test_fail "test 6: top-level update failed"
|
||||
|
||||
diff "$srcbase/topfile" "$HOME/topfile" >/dev/null \
|
||||
|| test_fail "test 6: top-level update content mismatch"
|
||||
|
||||
######################################################################
|
||||
# Test 7: --partial-dir with protocol < 29
|
||||
#
|
||||
# For protocol < 29, fnamecmp_type stays FNAMECMP_FNAME even when
|
||||
# fnamecmp is set to partialptr. The dirname/basename split must
|
||||
# NOT trigger in this case (guarded by "fnamecmp == fname").
|
||||
######################################################################
|
||||
|
||||
rm -f "$HOME/real-dir/file"
|
||||
make_testfile "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --protocol=28 --partial-dir=.rsync-partial \
|
||||
--rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 7: initial proto28 partial-dir transfer failed"
|
||||
|
||||
echo "partial-dir update" >> "$srcbase/dir/file"
|
||||
sleep 1
|
||||
touch "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --protocol=28 --partial-dir=.rsync-partial \
|
||||
--rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 7: proto28 partial-dir update through dirlink failed"
|
||||
|
||||
diff "$srcbase/dir/file" "$HOME/real-dir/file" >/dev/null \
|
||||
|| test_fail "test 7: proto28 partial-dir update content mismatch"
|
||||
|
||||
######################################################################
|
||||
# Test 8: Protocol < 29 basic directory symlink update
|
||||
#
|
||||
# Exercises the protocol < 29 code path and its fallback logic
|
||||
# (clearing basedir on retry).
|
||||
######################################################################
|
||||
|
||||
rm -f "$HOME/real-dir/file"
|
||||
make_testfile "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --protocol=28 \
|
||||
--rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 8: initial proto28 transfer failed"
|
||||
|
||||
echo "proto28 update" >> "$srcbase/dir/file"
|
||||
sleep 1
|
||||
touch "$srcbase/dir/file"
|
||||
|
||||
(cd "$srcbase" && $RSYNC -KRlptv --protocol=28 \
|
||||
--rsync-path="$RSYNC" dir/file localhost:) \
|
||||
|| test_fail "test 8: proto28 update through directory symlink failed"
|
||||
|
||||
diff "$srcbase/dir/file" "$HOME/real-dir/file" >/dev/null \
|
||||
|| test_fail "test 8: proto28 update content mismatch"
|
||||
|
||||
# The script would have aborted on error, so getting here means we've won.
|
||||
exit 0
|
||||
@@ -40,7 +40,7 @@ test_unsafe ..//../dest from/dir unsafe
|
||||
test_unsafe .. from/file safe
|
||||
test_unsafe ../.. from/file unsafe
|
||||
test_unsafe ..//.. from//file unsafe
|
||||
test_unsafe dir/.. from unsafe
|
||||
test_unsafe dir/.. from safe
|
||||
test_unsafe dir/../.. from unsafe
|
||||
test_unsafe dir/..//.. from unsafe
|
||||
|
||||
|
||||
@@ -38,10 +38,7 @@ EOF
|
||||
xls() {
|
||||
for fn in "${@}"; do
|
||||
runat "$fn" "$SHELL_PATH" <<EOF
|
||||
for x in *; do
|
||||
case "\$x" in SUNWattr_*) continue;; esac
|
||||
echo "\$x=\`cat \$x\`"
|
||||
done
|
||||
for x in *; do echo "\$x=\`cat \$x\`"; done
|
||||
EOF
|
||||
done
|
||||
}
|
||||
|
||||
5
tls.c
5
tls.c
@@ -49,9 +49,6 @@ int list_only = 0;
|
||||
int link_times = 0;
|
||||
int link_owner = 0;
|
||||
int nsec_times = 0;
|
||||
int safe_symlinks = 0;
|
||||
int copy_links = 0;
|
||||
int copy_unsafe_links = 0;
|
||||
|
||||
#ifdef SUPPORT_XATTRS
|
||||
|
||||
@@ -127,7 +124,7 @@ static void storetime(char *dest, size_t destsize, time_t t, int nsecs)
|
||||
{
|
||||
if (t) {
|
||||
int len;
|
||||
struct tm tmp, *mt = gmtime_r(&t, &tmp);
|
||||
struct tm *mt = gmtime(&t);
|
||||
|
||||
len = snprintf(dest, destsize,
|
||||
" %04d-%02d-%02d %02d:%02d:%02d",
|
||||
|
||||
102
token.c
102
token.c
@@ -291,14 +291,6 @@ static int32 simple_recv_token(int f, char **data)
|
||||
int32 i = read_int(f);
|
||||
if (i <= 0)
|
||||
return i;
|
||||
/* simple_send_token caps each literal chunk at CHUNK_SIZE;
|
||||
* reject anything larger so a hostile peer cannot drive the
|
||||
* read_buf below past our static CHUNK_SIZE buffer. */
|
||||
if (i > CHUNK_SIZE) {
|
||||
rprintf(FERROR, "invalid uncompressed token length %ld [%s]\n",
|
||||
(long)i, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
residue = i;
|
||||
}
|
||||
|
||||
@@ -501,52 +493,9 @@ static char *cbuf;
|
||||
static char *dbuf;
|
||||
|
||||
/* for decoding runs of tokens */
|
||||
#define MAX_TOKEN_INDEX ((int32)0x7ffffffe)
|
||||
|
||||
static int32 rx_token;
|
||||
static int32 rx_run;
|
||||
|
||||
static NORETURN void invalid_compressed_token(void)
|
||||
{
|
||||
rprintf(FERROR, "invalid token number in compressed stream\n");
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
|
||||
static int32 recv_compressed_token_num(int f, int32 flag)
|
||||
{
|
||||
if (flag & TOKEN_REL) {
|
||||
int32 incr = flag & 0x3f;
|
||||
if (rx_token > MAX_TOKEN_INDEX - incr)
|
||||
invalid_compressed_token();
|
||||
rx_token += incr;
|
||||
flag >>= 6;
|
||||
} else {
|
||||
rx_token = read_int(f);
|
||||
if (rx_token < 0 || rx_token > MAX_TOKEN_INDEX)
|
||||
invalid_compressed_token();
|
||||
}
|
||||
|
||||
if (flag & 1) {
|
||||
rx_run = read_byte(f);
|
||||
rx_run += read_byte(f) << 8;
|
||||
if (rx_run <= 0 || rx_token > MAX_TOKEN_INDEX - rx_run)
|
||||
invalid_compressed_token();
|
||||
recv_state = r_running;
|
||||
}
|
||||
|
||||
return -1 - rx_token;
|
||||
}
|
||||
|
||||
static int32 recv_compressed_token_run(void)
|
||||
{
|
||||
if (rx_run <= 0 || rx_token >= MAX_TOKEN_INDEX)
|
||||
invalid_compressed_token();
|
||||
++rx_token;
|
||||
if (--rx_run == 0)
|
||||
recv_state = r_idle;
|
||||
return -1 - rx_token;
|
||||
}
|
||||
|
||||
/* Receive a deflated token and inflate it */
|
||||
static int32 recv_deflated_token(int f, char **data)
|
||||
{
|
||||
@@ -637,7 +586,17 @@ static int32 recv_deflated_token(int f, char **data)
|
||||
}
|
||||
|
||||
/* here we have a token of some kind */
|
||||
return recv_compressed_token_num(f, flag);
|
||||
if (flag & TOKEN_REL) {
|
||||
rx_token += flag & 0x3f;
|
||||
flag >>= 6;
|
||||
} else
|
||||
rx_token = read_int(f);
|
||||
if (flag & 1) {
|
||||
rx_run = read_byte(f);
|
||||
rx_run += read_byte(f) << 8;
|
||||
recv_state = r_running;
|
||||
}
|
||||
return -1 - rx_token;
|
||||
|
||||
case r_inflating:
|
||||
rx_strm.next_out = (Bytef *)dbuf;
|
||||
@@ -657,7 +616,10 @@ static int32 recv_deflated_token(int f, char **data)
|
||||
break;
|
||||
|
||||
case r_running:
|
||||
return recv_compressed_token_run();
|
||||
++rx_token;
|
||||
if (--rx_run == 0)
|
||||
recv_state = r_idle;
|
||||
return -1 - rx_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -866,7 +828,17 @@ static int32 recv_zstd_token(int f, char **data)
|
||||
return 0;
|
||||
}
|
||||
/* here we have a token of some kind */
|
||||
return recv_compressed_token_num(f, flag);
|
||||
if (flag & TOKEN_REL) {
|
||||
rx_token += flag & 0x3f;
|
||||
flag >>= 6;
|
||||
} else
|
||||
rx_token = read_int(f);
|
||||
if (flag & 1) {
|
||||
rx_run = read_byte(f);
|
||||
rx_run += read_byte(f) << 8;
|
||||
recv_state = r_running;
|
||||
}
|
||||
return -1 - rx_token;
|
||||
|
||||
case r_inflated: /* zstd doesn't get into this state */
|
||||
break;
|
||||
@@ -897,7 +869,10 @@ static int32 recv_zstd_token(int f, char **data)
|
||||
break;
|
||||
|
||||
case r_running:
|
||||
return recv_compressed_token_run();
|
||||
++rx_token;
|
||||
if (--rx_run == 0)
|
||||
recv_state = r_idle;
|
||||
return -1 - rx_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1017,7 +992,17 @@ static int32 recv_compressed_token(int f, char **data)
|
||||
}
|
||||
|
||||
/* here we have a token of some kind */
|
||||
return recv_compressed_token_num(f, flag);
|
||||
if (flag & TOKEN_REL) {
|
||||
rx_token += flag & 0x3f;
|
||||
flag >>= 6;
|
||||
} else
|
||||
rx_token = read_int(f);
|
||||
if (flag & 1) {
|
||||
rx_run = read_byte(f);
|
||||
rx_run += read_byte(f) << 8;
|
||||
recv_state = r_running;
|
||||
}
|
||||
return -1 - rx_token;
|
||||
|
||||
case r_inflating:
|
||||
avail_out = LZ4_decompress_safe(next_in, dbuf, avail_in, size);
|
||||
@@ -1033,7 +1018,10 @@ static int32 recv_compressed_token(int f, char **data)
|
||||
break;
|
||||
|
||||
case r_running:
|
||||
return recv_compressed_token_run();
|
||||
++rx_token;
|
||||
if (--rx_run == 0)
|
||||
recv_state = r_idle;
|
||||
return -1 - rx_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ int am_root = 0;
|
||||
int am_sender = 1;
|
||||
int read_only = 1;
|
||||
int list_only = 0;
|
||||
int copy_links = 0;
|
||||
int copy_unsafe_links = 0;
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
|
||||
146
util1.c
146
util1.c
@@ -141,7 +141,7 @@ int set_times(const char *fname, STRUCT_STAT *stp)
|
||||
|
||||
#ifdef HAVE_UTIMENSAT
|
||||
#include "case_N.h"
|
||||
if (do_utimensat_at(fname, stp) == 0)
|
||||
if (do_utimensat(fname, stp) == 0)
|
||||
break;
|
||||
if (errno != ENOSYS)
|
||||
return -1;
|
||||
@@ -336,13 +336,7 @@ static int unlink_and_reopen(const char *dest, mode_t mode)
|
||||
mode |= S_IWUSR;
|
||||
#endif
|
||||
mode &= INITACCESSPERMS;
|
||||
/* Use do_open_at so the create/truncate goes through a secure
|
||||
* parent dirfd in the daemon-no-chroot deployment. Otherwise
|
||||
* an attacker could swap a parent component with a symlink in
|
||||
* the window between robust_unlink (which uses do_unlink_at,
|
||||
* already secure) and the create here, and redirect the new
|
||||
* file outside the module. */
|
||||
if ((ofd = do_open_at(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
|
||||
if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
|
||||
int save_errno = errno;
|
||||
rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest));
|
||||
errno = save_errno;
|
||||
@@ -366,23 +360,12 @@ static int unlink_and_reopen(const char *dest, mode_t mode)
|
||||
* --copy-dest options. */
|
||||
int copy_file(const char *source, const char *dest, int tmpfilefd, mode_t mode)
|
||||
{
|
||||
extern int am_daemon, am_chrooted;
|
||||
int ifd, ofd;
|
||||
char buf[1024 * 8];
|
||||
int len; /* Number of bytes read into `buf'. */
|
||||
OFF_T prealloc_len = 0, offset = 0;
|
||||
|
||||
/* On a daemon without chroot, route the source open through
|
||||
* secure_relative_open so a parent-symlink on the source path
|
||||
* (e.g. --copy-dest=cd where cd is a symlink to an outside
|
||||
* directory) cannot redirect the read to a file the daemon can
|
||||
* see but the attacker should not. Plain do_open_nofollow only
|
||||
* refuses a final-component symlink; parents are still followed. */
|
||||
if (am_daemon && !am_chrooted && source && *source && source[0] != '/')
|
||||
ifd = secure_relative_open(NULL, source, O_RDONLY | O_NOFOLLOW, 0);
|
||||
else
|
||||
ifd = do_open_nofollow(source, O_RDONLY);
|
||||
if (ifd < 0) {
|
||||
if ((ifd = do_open(source, O_RDONLY, 0)) < 0) {
|
||||
int save_errno = errno;
|
||||
rsyserr(FERROR_XFER, errno, "open %s", full_fname(source));
|
||||
errno = save_errno;
|
||||
@@ -496,13 +479,13 @@ int copy_file(const char *source, const char *dest, int tmpfilefd, mode_t mode)
|
||||
int robust_unlink(const char *fname)
|
||||
{
|
||||
#ifndef ETXTBSY
|
||||
return do_unlink_at(fname);
|
||||
return do_unlink(fname);
|
||||
#else
|
||||
static int counter = 1;
|
||||
int rc, pos, start;
|
||||
char path[MAXPATHLEN];
|
||||
|
||||
rc = do_unlink_at(fname);
|
||||
rc = do_unlink(fname);
|
||||
if (rc == 0 || errno != ETXTBSY)
|
||||
return rc;
|
||||
|
||||
@@ -532,7 +515,7 @@ int robust_unlink(const char *fname)
|
||||
}
|
||||
|
||||
/* maybe we should return rename()'s exit status? Nah. */
|
||||
if (do_rename_at(fname, path) != 0) {
|
||||
if (do_rename(fname, path) != 0) {
|
||||
errno = ETXTBSY;
|
||||
return -1;
|
||||
}
|
||||
@@ -555,7 +538,7 @@ int robust_rename(const char *from, const char *to, const char *partialptr,
|
||||
return 0;
|
||||
|
||||
while (tries--) {
|
||||
if (do_rename_at(from, to) == 0)
|
||||
if (do_rename(from, to) == 0)
|
||||
return 0;
|
||||
|
||||
switch (errno) {
|
||||
@@ -576,7 +559,7 @@ int robust_rename(const char *from, const char *to, const char *partialptr,
|
||||
}
|
||||
if (copy_file(from, to, -1, mode) != 0)
|
||||
return -2;
|
||||
do_unlink_at(from);
|
||||
do_unlink(from);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
@@ -959,7 +942,7 @@ int count_dir_elements(const char *p)
|
||||
* resulting name would be empty, returns ".". */
|
||||
int clean_fname(char *name, int flags)
|
||||
{
|
||||
char *limit = name, *t = name, *f = name;
|
||||
char *limit = name - 1, *t = name, *f = name;
|
||||
int anchored;
|
||||
|
||||
if (!name)
|
||||
@@ -1004,13 +987,9 @@ int clean_fname(char *name, int flags)
|
||||
f += 2;
|
||||
continue;
|
||||
}
|
||||
/* backing up for ".." — avoid reading before 'name' */
|
||||
while (s > limit && s[-1] != '/')
|
||||
s--;
|
||||
|
||||
/* If found prior '/', or we reached the start, adjust t. */
|
||||
if (s != t - 1 && (s <= name || *s == '/')) {
|
||||
t = (s == name) ? name : s + 1;
|
||||
while (s > limit && *--s != '/') {}
|
||||
if (s != t - 1 && (s < name || *s == '/')) {
|
||||
t = s + 1;
|
||||
f += 2;
|
||||
continue;
|
||||
}
|
||||
@@ -1133,7 +1112,6 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, i
|
||||
* Also cleans the path using the clean_fname() function. */
|
||||
int change_dir(const char *dir, int set_path_only)
|
||||
{
|
||||
extern int am_daemon, am_chrooted;
|
||||
static int initialised, skipped_chdir;
|
||||
unsigned int len;
|
||||
|
||||
@@ -1172,57 +1150,10 @@ int change_dir(const char *dir, int set_path_only)
|
||||
curr_dir[curr_dir_len++] = '/';
|
||||
memcpy(curr_dir + curr_dir_len, dir, len + 1);
|
||||
|
||||
if (!set_path_only) {
|
||||
int chdir_failed;
|
||||
/* In the daemon-without-chroot deployment we must not
|
||||
* follow a symlink in any component of the chdir
|
||||
* target -- otherwise CWD escapes the module and
|
||||
* every subsequent path-relative syscall (open,
|
||||
* chmod, lchown, ...) inherits the escape, which
|
||||
* defeats secure_relative_open's RESOLVE_BENEATH
|
||||
* anchor and re-opens the CVE-2026-29518 class of
|
||||
* symlink TOCTOU attacks. Use the secure resolver
|
||||
* to get a confined dirfd, then fchdir() to it.
|
||||
*
|
||||
* If skipped_chdir is set, a previous CD_SKIP_CHDIR
|
||||
* call buffered an absolute prefix in curr_dir
|
||||
* (e.g. change_pathname's CD_SKIP_CHDIR to orig_dir)
|
||||
* without syncing the kernel's CWD. Resolve `dir`
|
||||
* relative to that prefix as basedir so the secure
|
||||
* branch still anchors at the operator-trusted
|
||||
* directory rather than wherever the kernel CWD
|
||||
* happens to be. */
|
||||
if (am_daemon && !am_chrooted) {
|
||||
const char *basedir = NULL;
|
||||
char prefix[MAXPATHLEN];
|
||||
int dfd;
|
||||
if (skipped_chdir) {
|
||||
if (save_dir_len >= sizeof prefix) {
|
||||
errno = ENAMETOOLONG;
|
||||
chdir_failed = 1;
|
||||
goto chdir_cleanup;
|
||||
}
|
||||
memcpy(prefix, curr_dir, save_dir_len);
|
||||
prefix[save_dir_len] = '\0';
|
||||
basedir = prefix;
|
||||
}
|
||||
dfd = secure_relative_open(basedir, dir,
|
||||
O_RDONLY | O_DIRECTORY, 0);
|
||||
if (dfd < 0) {
|
||||
chdir_failed = 1;
|
||||
} else {
|
||||
chdir_failed = fchdir(dfd) != 0;
|
||||
close(dfd);
|
||||
}
|
||||
} else {
|
||||
chdir_failed = chdir(curr_dir) != 0;
|
||||
}
|
||||
chdir_cleanup:
|
||||
if (chdir_failed) {
|
||||
curr_dir_len = save_dir_len;
|
||||
curr_dir[curr_dir_len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
if (!set_path_only && chdir(curr_dir)) {
|
||||
curr_dir_len = save_dir_len;
|
||||
curr_dir[curr_dir_len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
skipped_chdir = set_path_only;
|
||||
}
|
||||
@@ -1350,20 +1281,20 @@ int handle_partial_dir(const char *fname, int create)
|
||||
dir = partial_fname;
|
||||
if (create) {
|
||||
STRUCT_STAT st;
|
||||
int statret = do_lstat_at(dir, &st);
|
||||
int statret = do_lstat(dir, &st);
|
||||
if (statret == 0 && !S_ISDIR(st.st_mode)) {
|
||||
if (do_unlink_at(dir) < 0) {
|
||||
if (do_unlink(dir) < 0) {
|
||||
*fn = '/';
|
||||
return 0;
|
||||
}
|
||||
statret = -1;
|
||||
}
|
||||
if (statret < 0 && do_mkdir_at(dir, 0700) < 0) {
|
||||
if (statret < 0 && do_mkdir(dir, 0700) < 0) {
|
||||
*fn = '/';
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
do_rmdir_at(dir);
|
||||
do_rmdir(dir);
|
||||
*fn = '/';
|
||||
|
||||
return 1;
|
||||
@@ -1387,14 +1318,7 @@ int handle_partial_dir(const char *fname, int create)
|
||||
*
|
||||
* "src" is the top source directory currently applicable at the level
|
||||
* of the referenced symlink. This is usually the symlink's full path
|
||||
* (including its name), as referenced from the root of the transfer.
|
||||
*
|
||||
* NOTE: this also rejects dest names with a .. component in other
|
||||
* than the first component of the name ie. it rejects names such as
|
||||
* a/b/../x/y. This needs to be done as the leading subpaths 'a' or
|
||||
* 'b' could later be replaced with symlinks such as a link to '.'
|
||||
* resulting in the link being transferred now becoming unsafe
|
||||
*/
|
||||
* (including its name), as referenced from the root of the transfer. */
|
||||
int unsafe_symlink(const char *dest, const char *src)
|
||||
{
|
||||
const char *name, *slash;
|
||||
@@ -1404,23 +1328,6 @@ int unsafe_symlink(const char *dest, const char *src)
|
||||
if (!dest || !*dest || *dest == '/')
|
||||
return 1;
|
||||
|
||||
// reject destinations with /../ in the name other than at the start of the name
|
||||
const char *dest2 = dest;
|
||||
while (strncmp(dest2, "../", 3) == 0) {
|
||||
dest2 += 3;
|
||||
while (*dest2 == '/') {
|
||||
// allow for ..//..///../foo
|
||||
dest2++;
|
||||
}
|
||||
}
|
||||
if (strstr(dest2, "/../"))
|
||||
return 1;
|
||||
|
||||
// reject if the destination ends in /..
|
||||
const size_t dlen = strlen(dest);
|
||||
if (dlen > 3 && strcmp(&dest[dlen-3], "/..") == 0)
|
||||
return 1;
|
||||
|
||||
/* find out what our safety margin is */
|
||||
for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
|
||||
/* ".." segment starts the count over. "." segment is ignored. */
|
||||
@@ -1458,13 +1365,8 @@ char *timestring(time_t t)
|
||||
static int ndx = 0;
|
||||
static char buffers[4][20]; /* We support 4 simultaneous timestring results. */
|
||||
char *TimeBuf = buffers[ndx = (ndx + 1) % 4];
|
||||
struct tm tmp, *tm = localtime_r(&t, &tmp);
|
||||
int len;
|
||||
if (!tm) {
|
||||
strlcpy(TimeBuf, "(time out of range)", sizeof buffers[0]);
|
||||
return TimeBuf;
|
||||
}
|
||||
len = snprintf(TimeBuf, sizeof buffers[0], "%4d/%02d/%02d %02d:%02d:%02d",
|
||||
struct tm *tm = localtime(&t);
|
||||
int len = snprintf(TimeBuf, sizeof buffers[0], "%4d/%02d/%02d %02d:%02d:%02d",
|
||||
(int)tm->tm_year + 1900, (int)tm->tm_mon + 1, (int)tm->tm_mday,
|
||||
(int)tm->tm_hour, (int)tm->tm_min, (int)tm->tm_sec);
|
||||
assert(len > 0); /* Silence gcc warning */
|
||||
@@ -1788,8 +1690,6 @@ void *expand_item_list(item_list *lp, size_t item_size, const char *desc, int in
|
||||
new_ptr == lp->items ? " not" : "");
|
||||
}
|
||||
|
||||
memset((char *)new_ptr + lp->malloced * item_size, 0,
|
||||
(expand_size - lp->malloced) * item_size);
|
||||
lp->items = new_ptr;
|
||||
lp->malloced = expand_size;
|
||||
}
|
||||
|
||||
4
util2.c
4
util2.c
@@ -79,7 +79,9 @@ void *my_alloc(void *ptr, size_t num, size_t size, const char *file, int line)
|
||||
who_am_i(), do_big_num(max_alloc, 0, NULL), src_file(file), line);
|
||||
exit_cleanup(RERR_MALLOC);
|
||||
}
|
||||
if (!ptr || ptr == do_calloc)
|
||||
if (!ptr)
|
||||
ptr = malloc(num * size);
|
||||
else if (ptr == do_calloc)
|
||||
ptr = calloc(num, size);
|
||||
else
|
||||
ptr = realloc(ptr, num * size);
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define RSYNC_VERSION "3.2.7"
|
||||
#define RSYNC_VERSION "3.3.0pre1"
|
||||
#define MAINTAINER_TZ_OFFSET -7.0
|
||||
|
||||
@@ -32,9 +32,7 @@ int fnmatch_errors = 0;
|
||||
|
||||
int wildmatch_errors = 0;
|
||||
|
||||
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L
|
||||
typedef char bool;
|
||||
#endif
|
||||
|
||||
int output_iterations = 0;
|
||||
int explode_mod = 0;
|
||||
|
||||
35
xattrs.c
35
xattrs.c
@@ -697,13 +697,6 @@ int recv_xattr_request(struct file_struct *file, int f_in)
|
||||
rxa = lst->items;
|
||||
num = 0;
|
||||
while ((rel_pos = read_varint(f_in)) != 0) {
|
||||
/* Detect signed overflow before the accumulating add. A hostile
|
||||
* peer could otherwise wrap 'num' to land on an arbitrary value. */
|
||||
if ((rel_pos > 0 && num > INT_MAX - rel_pos)
|
||||
|| (rel_pos < 0 && num < INT_MIN - rel_pos)) {
|
||||
rprintf(FERROR, "xattr rel_pos accumulation overflow [%s]\n", who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
num += rel_pos;
|
||||
if (am_sender) {
|
||||
/* The sender-related num values are only in order on the sender.
|
||||
@@ -749,7 +742,7 @@ int recv_xattr_request(struct file_struct *file, int f_in)
|
||||
}
|
||||
|
||||
old_datum = rxa->datum;
|
||||
rxa->datum_len = read_varint_size(f_in, MAX_WIRE_XATTR_DATALEN, "xattr datum_len");
|
||||
rxa->datum_len = read_varint(f_in);
|
||||
|
||||
if (SIZE_MAX - rxa->name_len < rxa->datum_len)
|
||||
overflow_exit("recv_xattr_request");
|
||||
@@ -790,8 +783,7 @@ void receive_xattr(int f, struct file_struct *file)
|
||||
return;
|
||||
}
|
||||
|
||||
count = read_varint_bounded(f, 0, MAX_WIRE_XATTR_COUNT, "xattr count");
|
||||
if (count != 0) {
|
||||
if ((count = read_varint(f)) != 0) {
|
||||
(void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
|
||||
temp_xattr.count = 0;
|
||||
}
|
||||
@@ -799,8 +791,8 @@ void receive_xattr(int f, struct file_struct *file)
|
||||
for (num = 1; num <= count; num++) {
|
||||
char *ptr, *name;
|
||||
rsync_xa *rxa;
|
||||
size_t name_len = read_varint_size(f, MAX_WIRE_XATTR_NAMELEN, "xattr name_len");
|
||||
size_t datum_len = read_varint_size(f, MAX_WIRE_XATTR_DATALEN, "xattr datum_len");
|
||||
size_t name_len = read_varint(f);
|
||||
size_t datum_len = read_varint(f);
|
||||
size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + (size_t)xattr_sum_len : datum_len;
|
||||
size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
|
||||
if (SIZE_MAX - dget_len < extra_len || SIZE_MAX - dget_len - extra_len < name_len)
|
||||
@@ -868,8 +860,8 @@ void receive_xattr(int f, struct file_struct *file)
|
||||
rxa->num = num;
|
||||
}
|
||||
|
||||
if (need_sort && temp_xattr.count > 1)
|
||||
qsort(temp_xattr.items, temp_xattr.count, sizeof (rsync_xa), rsync_xal_compare_names);
|
||||
if (need_sort && count > 1)
|
||||
qsort(temp_xattr.items, count, sizeof (rsync_xa), rsync_xal_compare_names);
|
||||
|
||||
ndx = rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
|
||||
|
||||
@@ -1094,7 +1086,7 @@ int set_xattr(const char *fname, const struct file_struct *file, const char *fna
|
||||
&& !S_ISLNK(sxp->st.st_mode)
|
||||
#endif
|
||||
&& access(fname, W_OK) < 0
|
||||
&& do_chmod_at(fname, (sxp->st.st_mode & CHMOD_BITS) | S_IWUSR) == 0)
|
||||
&& do_chmod(fname, (sxp->st.st_mode & CHMOD_BITS) | S_IWUSR) == 0)
|
||||
added_write_perm = 1;
|
||||
|
||||
ndx = F_XATTR(file);
|
||||
@@ -1102,7 +1094,7 @@ int set_xattr(const char *fname, const struct file_struct *file, const char *fna
|
||||
lst = &glst->xa_items;
|
||||
int return_value = rsync_xal_set(fname, lst, fnamecmp, sxp);
|
||||
if (added_write_perm) /* remove the temporary write permission */
|
||||
do_chmod_at(fname, sxp->st.st_mode);
|
||||
do_chmod(fname, sxp->st.st_mode);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
@@ -1219,7 +1211,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
|
||||
mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
|
||||
| (S_ISDIR(fst.st_mode) ? 0700 : 0600);
|
||||
if (fst.st_mode != mode)
|
||||
do_chmod_at(fname, mode);
|
||||
do_chmod(fname, mode);
|
||||
if (!IS_DEVICE(fst.st_mode))
|
||||
fst.st_rdev = 0; /* just in case */
|
||||
|
||||
@@ -1257,12 +1249,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
|
||||
|
||||
int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
|
||||
{
|
||||
/* Use the *_at variants so that on a daemon-no-chroot deployment
|
||||
* the metadata read goes through a secure parent dirfd instead
|
||||
* of bare path resolution. The *_at wrappers fall through to
|
||||
* plain do_stat outside the daemon-no-chroot context, so this
|
||||
* change is transparent for non-daemon use. */
|
||||
int ret = do_stat_at(fname, fst);
|
||||
int ret = do_stat(fname, fst);
|
||||
if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
|
||||
xst->st_mode = 0;
|
||||
return ret;
|
||||
@@ -1270,7 +1257,7 @@ int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
|
||||
|
||||
int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
|
||||
{
|
||||
int ret = do_lstat_at(fname, fst);
|
||||
int ret = do_lstat(fname, fst);
|
||||
if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
|
||||
xst->st_mode = 0;
|
||||
return ret;
|
||||
|
||||
@@ -62,7 +62,10 @@ local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
|
||||
#endif
|
||||
|
||||
/* ========================================================================= */
|
||||
uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len)
|
||||
uLong ZEXPORT adler32(adler, buf, len)
|
||||
uLong adler;
|
||||
const Bytef *buf;
|
||||
uInt len;
|
||||
{
|
||||
unsigned long sum2;
|
||||
unsigned n;
|
||||
@@ -130,7 +133,10 @@ uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2)
|
||||
local uLong adler32_combine_(adler1, adler2, len2)
|
||||
uLong adler1;
|
||||
uLong adler2;
|
||||
z_off64_t len2;
|
||||
{
|
||||
unsigned long sum1;
|
||||
unsigned long sum2;
|
||||
@@ -156,12 +162,18 @@ local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2)
|
||||
uLong ZEXPORT adler32_combine(adler1, adler2, len2)
|
||||
uLong adler1;
|
||||
uLong adler2;
|
||||
z_off_t len2;
|
||||
{
|
||||
return adler32_combine_(adler1, adler2, len2);
|
||||
}
|
||||
|
||||
uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2)
|
||||
uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
|
||||
uLong adler1;
|
||||
uLong adler2;
|
||||
z_off64_t len2;
|
||||
{
|
||||
return adler32_combine_(adler1, adler2, len2);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,12 @@
|
||||
memory, Z_BUF_ERROR if there was not enough room in the output buffer,
|
||||
Z_STREAM_ERROR if the level parameter is invalid.
|
||||
*/
|
||||
int ZEXPORT compress2 (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)
|
||||
int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
|
||||
Bytef *dest;
|
||||
uLongf *destLen;
|
||||
const Bytef *source;
|
||||
uLong sourceLen;
|
||||
int level;
|
||||
{
|
||||
z_stream stream;
|
||||
int err;
|
||||
@@ -54,7 +59,11 @@ int ZEXPORT compress2 (Bytef *dest, uLongf *destLen, const Bytef *source, uLong
|
||||
|
||||
/* ===========================================================================
|
||||
*/
|
||||
int ZEXPORT compress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)
|
||||
int ZEXPORT compress (dest, destLen, source, sourceLen)
|
||||
Bytef *dest;
|
||||
uLongf *destLen;
|
||||
const Bytef *source;
|
||||
uLong sourceLen;
|
||||
{
|
||||
return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
|
||||
}
|
||||
@@ -63,7 +72,8 @@ int ZEXPORT compress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong s
|
||||
If the default memLevel or windowBits for deflateInit() is changed, then
|
||||
this function needs to be updated.
|
||||
*/
|
||||
uLong ZEXPORT compressBound (uLong sourceLen)
|
||||
uLong ZEXPORT compressBound (sourceLen)
|
||||
uLong sourceLen;
|
||||
{
|
||||
return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
|
||||
(sourceLen >> 25) + 13;
|
||||
|
||||
46
zlib/crc32.c
46
zlib/crc32.c
@@ -87,7 +87,7 @@ local void make_crc_table OF((void));
|
||||
allow for word-at-a-time CRC calculation for both big-endian and little-
|
||||
endian machines, where a word is four bytes.
|
||||
*/
|
||||
local void make_crc_table(void)
|
||||
local void make_crc_table()
|
||||
{
|
||||
z_crc_t c;
|
||||
int n, k;
|
||||
@@ -164,7 +164,9 @@ local void make_crc_table(void)
|
||||
}
|
||||
|
||||
#ifdef MAKECRCH
|
||||
local void write_table(FILE *out, const z_crc_t FAR *table)
|
||||
local void write_table(out, table)
|
||||
FILE *out;
|
||||
const z_crc_t FAR *table;
|
||||
{
|
||||
int n;
|
||||
|
||||
@@ -185,7 +187,7 @@ local void write_table(FILE *out, const z_crc_t FAR *table)
|
||||
/* =========================================================================
|
||||
* This function can be used by asm versions of crc32()
|
||||
*/
|
||||
const z_crc_t FAR * ZEXPORT get_crc_table(void)
|
||||
const z_crc_t FAR * ZEXPORT get_crc_table()
|
||||
{
|
||||
#ifdef DYNAMIC_CRC_TABLE
|
||||
if (crc_table_empty)
|
||||
@@ -199,7 +201,10 @@ const z_crc_t FAR * ZEXPORT get_crc_table(void)
|
||||
#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
|
||||
|
||||
/* ========================================================================= */
|
||||
unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, uInt len)
|
||||
unsigned long ZEXPORT crc32(crc, buf, len)
|
||||
unsigned long crc;
|
||||
const unsigned char FAR *buf;
|
||||
uInt len;
|
||||
{
|
||||
if (buf == Z_NULL) return 0UL;
|
||||
|
||||
@@ -239,7 +244,10 @@ unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, uIn
|
||||
#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
|
||||
|
||||
/* ========================================================================= */
|
||||
local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf, unsigned len)
|
||||
local unsigned long crc32_little(crc, buf, len)
|
||||
unsigned long crc;
|
||||
const unsigned char FAR *buf;
|
||||
unsigned len;
|
||||
{
|
||||
register z_crc_t c;
|
||||
register const z_crc_t FAR *buf4;
|
||||
@@ -276,7 +284,10 @@ local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf
|
||||
#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
|
||||
|
||||
/* ========================================================================= */
|
||||
local unsigned long crc32_big(unsigned long crc, const unsigned char FAR *buf, unsigned len)
|
||||
local unsigned long crc32_big(crc, buf, len)
|
||||
unsigned long crc;
|
||||
const unsigned char FAR *buf;
|
||||
unsigned len;
|
||||
{
|
||||
register z_crc_t c;
|
||||
register const z_crc_t FAR *buf4;
|
||||
@@ -311,7 +322,9 @@ local unsigned long crc32_big(unsigned long crc, const unsigned char FAR *buf, u
|
||||
#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */
|
||||
|
||||
/* ========================================================================= */
|
||||
local unsigned long gf2_matrix_times(unsigned long *mat, unsigned long vec)
|
||||
local unsigned long gf2_matrix_times(mat, vec)
|
||||
unsigned long *mat;
|
||||
unsigned long vec;
|
||||
{
|
||||
unsigned long sum;
|
||||
|
||||
@@ -326,7 +339,9 @@ local unsigned long gf2_matrix_times(unsigned long *mat, unsigned long vec)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
local void gf2_matrix_square(unsigned long *square, unsigned long *mat)
|
||||
local void gf2_matrix_square(square, mat)
|
||||
unsigned long *square;
|
||||
unsigned long *mat;
|
||||
{
|
||||
int n;
|
||||
|
||||
@@ -335,7 +350,10 @@ local void gf2_matrix_square(unsigned long *square, unsigned long *mat)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2)
|
||||
local uLong crc32_combine_(crc1, crc2, len2)
|
||||
uLong crc1;
|
||||
uLong crc2;
|
||||
z_off64_t len2;
|
||||
{
|
||||
int n;
|
||||
unsigned long row;
|
||||
@@ -388,12 +406,18 @@ local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2)
|
||||
uLong ZEXPORT crc32_combine(crc1, crc2, len2)
|
||||
uLong crc1;
|
||||
uLong crc2;
|
||||
z_off_t len2;
|
||||
{
|
||||
return crc32_combine_(crc1, crc2, len2);
|
||||
}
|
||||
|
||||
uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2)
|
||||
uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
|
||||
uLong crc1;
|
||||
uLong crc2;
|
||||
z_off64_t len2;
|
||||
{
|
||||
return crc32_combine_(crc1, crc2, len2);
|
||||
}
|
||||
|
||||
124
zlib/deflate.c
124
zlib/deflate.c
@@ -200,8 +200,11 @@ struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
|
||||
zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version,
|
||||
int stream_size)
|
||||
int ZEXPORT deflateInit_(strm, level, version, stream_size)
|
||||
z_streamp strm;
|
||||
int level;
|
||||
const char *version;
|
||||
int stream_size;
|
||||
{
|
||||
return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
|
||||
Z_DEFAULT_STRATEGY, version, stream_size);
|
||||
@@ -209,8 +212,16 @@ int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version,
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, int windowBits,
|
||||
int memLevel, int strategy, const char *version, int stream_size)
|
||||
int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
|
||||
version, stream_size)
|
||||
z_streamp strm;
|
||||
int level;
|
||||
int method;
|
||||
int windowBits;
|
||||
int memLevel;
|
||||
int strategy;
|
||||
const char *version;
|
||||
int stream_size;
|
||||
{
|
||||
deflate_state *s;
|
||||
int wrap = 1;
|
||||
@@ -348,8 +359,10 @@ int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, int windowBits,
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary,
|
||||
uInt dictLength)
|
||||
int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
|
||||
z_streamp strm;
|
||||
const Bytef *dictionary;
|
||||
uInt dictLength;
|
||||
{
|
||||
deflate_state *s;
|
||||
uInt str, n;
|
||||
@@ -415,7 +428,8 @@ int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary,
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateResetKeep (z_streamp strm)
|
||||
int ZEXPORT deflateResetKeep (strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
deflate_state *s;
|
||||
|
||||
@@ -449,7 +463,8 @@ int ZEXPORT deflateResetKeep (z_streamp strm)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateReset (z_streamp strm)
|
||||
int ZEXPORT deflateReset (strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -460,7 +475,9 @@ int ZEXPORT deflateReset (z_streamp strm)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateSetHeader (z_streamp strm, gz_headerp head)
|
||||
int ZEXPORT deflateSetHeader (strm, head)
|
||||
z_streamp strm;
|
||||
gz_headerp head;
|
||||
{
|
||||
if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
|
||||
if (strm->state->wrap != 2) return Z_STREAM_ERROR;
|
||||
@@ -469,7 +486,10 @@ int ZEXPORT deflateSetHeader (z_streamp strm, gz_headerp head)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflatePending (z_streamp strm, unsigned *pending, int *bits)
|
||||
int ZEXPORT deflatePending (strm, pending, bits)
|
||||
unsigned *pending;
|
||||
int *bits;
|
||||
z_streamp strm;
|
||||
{
|
||||
if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
|
||||
if (pending != Z_NULL)
|
||||
@@ -480,7 +500,10 @@ int ZEXPORT deflatePending (z_streamp strm, unsigned *pending, int *bits)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflatePrime (z_streamp strm, int bits, int value)
|
||||
int ZEXPORT deflatePrime (strm, bits, value)
|
||||
z_streamp strm;
|
||||
int bits;
|
||||
int value;
|
||||
{
|
||||
deflate_state *s;
|
||||
int put;
|
||||
@@ -503,7 +526,10 @@ int ZEXPORT deflatePrime (z_streamp strm, int bits, int value)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateParams(z_streamp strm, int level, int strategy)
|
||||
int ZEXPORT deflateParams(strm, level, strategy)
|
||||
z_streamp strm;
|
||||
int level;
|
||||
int strategy;
|
||||
{
|
||||
deflate_state *s;
|
||||
compress_func func;
|
||||
@@ -541,8 +567,12 @@ int ZEXPORT deflateParams(z_streamp strm, int level, int strategy)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy,
|
||||
int nice_length, int max_chain)
|
||||
int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
|
||||
z_streamp strm;
|
||||
int good_length;
|
||||
int max_lazy;
|
||||
int nice_length;
|
||||
int max_chain;
|
||||
{
|
||||
deflate_state *s;
|
||||
|
||||
@@ -572,7 +602,9 @@ int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy,
|
||||
* upper bound of about 14% expansion does not seem onerous for output buffer
|
||||
* allocation.
|
||||
*/
|
||||
uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen)
|
||||
uLong ZEXPORT deflateBound(strm, sourceLen)
|
||||
z_streamp strm;
|
||||
uLong sourceLen;
|
||||
{
|
||||
deflate_state *s;
|
||||
uLong complen, wraplen;
|
||||
@@ -632,7 +664,9 @@ uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen)
|
||||
* IN assertion: the stream state is correct and there is enough room in
|
||||
* pending_buf.
|
||||
*/
|
||||
local void putShortMSB (deflate_state *s, uInt b)
|
||||
local void putShortMSB (s, b)
|
||||
deflate_state *s;
|
||||
uInt b;
|
||||
{
|
||||
put_byte(s, (Byte)(b >> 8));
|
||||
put_byte(s, (Byte)(b & 0xff));
|
||||
@@ -644,7 +678,8 @@ local void putShortMSB (deflate_state *s, uInt b)
|
||||
* to avoid allocating a large strm->next_out buffer and copying into it.
|
||||
* (See also read_buf()).
|
||||
*/
|
||||
local void flush_pending(z_streamp strm)
|
||||
local void flush_pending(strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
unsigned len;
|
||||
deflate_state *s = strm->state;
|
||||
@@ -666,7 +701,9 @@ local void flush_pending(z_streamp strm)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflate (z_streamp strm, int flush)
|
||||
int ZEXPORT deflate (strm, flush)
|
||||
z_streamp strm;
|
||||
int flush;
|
||||
{
|
||||
int old_flush; /* value of flush param for previous deflate call */
|
||||
deflate_state *s;
|
||||
@@ -978,7 +1015,8 @@ int ZEXPORT deflate (z_streamp strm, int flush)
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
int ZEXPORT deflateEnd (z_streamp strm)
|
||||
int ZEXPORT deflateEnd (strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
int status;
|
||||
|
||||
@@ -1012,7 +1050,9 @@ int ZEXPORT deflateEnd (z_streamp strm)
|
||||
* To simplify the source, this is not supported for 16-bit MSDOS (which
|
||||
* doesn't have enough memory anyway to duplicate compression states).
|
||||
*/
|
||||
int ZEXPORT deflateCopy (z_streamp dest, z_streamp source)
|
||||
int ZEXPORT deflateCopy (dest, source)
|
||||
z_streamp dest;
|
||||
z_streamp source;
|
||||
{
|
||||
#ifdef MAXSEG_64K
|
||||
return Z_STREAM_ERROR;
|
||||
@@ -1069,7 +1109,10 @@ int ZEXPORT deflateCopy (z_streamp dest, z_streamp source)
|
||||
* allocating a large strm->next_in buffer and copying from it.
|
||||
* (See also flush_pending()).
|
||||
*/
|
||||
local int read_buf(z_streamp strm, Bytef *buf, unsigned size)
|
||||
local int read_buf(strm, buf, size)
|
||||
z_streamp strm;
|
||||
Bytef *buf;
|
||||
unsigned size;
|
||||
{
|
||||
unsigned len = strm->avail_in;
|
||||
|
||||
@@ -1096,7 +1139,8 @@ local int read_buf(z_streamp strm, Bytef *buf, unsigned size)
|
||||
/* ===========================================================================
|
||||
* Initialize the "longest match" routines for a new zlib stream
|
||||
*/
|
||||
local void lm_init (deflate_state *s)
|
||||
local void lm_init (s)
|
||||
deflate_state *s;
|
||||
{
|
||||
s->window_size = (ulg)2L*s->w_size;
|
||||
|
||||
@@ -1137,7 +1181,9 @@ local void lm_init (deflate_state *s)
|
||||
/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
|
||||
* match.S. The code will be functionally equivalent.
|
||||
*/
|
||||
local uInt longest_match(deflate_state *s, IPos cur_match)
|
||||
local uInt longest_match(s, cur_match)
|
||||
deflate_state *s;
|
||||
IPos cur_match; /* current match */
|
||||
{
|
||||
unsigned chain_length = s->max_chain_length;/* max hash chain length */
|
||||
register Bytef *scan = s->window + s->strstart; /* current string */
|
||||
@@ -1284,7 +1330,9 @@ local uInt longest_match(deflate_state *s, IPos cur_match)
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Optimized version for FASTEST only
|
||||
*/
|
||||
local uInt longest_match(deflate_state *s, IPos cur_match)
|
||||
local uInt longest_match(s, cur_match)
|
||||
deflate_state *s;
|
||||
IPos cur_match; /* current match */
|
||||
{
|
||||
register Bytef *scan = s->window + s->strstart; /* current string */
|
||||
register Bytef *match; /* matched string */
|
||||
@@ -1341,7 +1389,10 @@ local uInt longest_match(deflate_state *s, IPos cur_match)
|
||||
/* ===========================================================================
|
||||
* Check that the match at match_start is indeed a match.
|
||||
*/
|
||||
local void check_match(deflate_state *s, IPos start, IPos match, int length)
|
||||
local void check_match(s, start, match, length)
|
||||
deflate_state *s;
|
||||
IPos start, match;
|
||||
int length;
|
||||
{
|
||||
/* check that the match is indeed a match */
|
||||
if (zmemcmp(s->window + match,
|
||||
@@ -1372,7 +1423,8 @@ local void check_match(deflate_state *s, IPos start, IPos match, int length)
|
||||
* performed for at least two bytes (required for the zip translate_eol
|
||||
* option -- not supported here).
|
||||
*/
|
||||
local void fill_window(deflate_state *s)
|
||||
local void fill_window(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
register unsigned n, m;
|
||||
register Posf *p;
|
||||
@@ -1545,7 +1597,9 @@ local void fill_window(deflate_state *s)
|
||||
* NOTE: this function should be optimized to avoid extra copying from
|
||||
* window to pending_buf.
|
||||
*/
|
||||
local block_state deflate_stored(deflate_state *s, int flush)
|
||||
local block_state deflate_stored(s, flush)
|
||||
deflate_state *s;
|
||||
int flush;
|
||||
{
|
||||
/* Stored blocks are limited to 0xffff bytes, pending_buf is limited
|
||||
* to pending_buf_size, and each stored block has a 5 byte header:
|
||||
@@ -1616,7 +1670,9 @@ local block_state deflate_stored(deflate_state *s, int flush)
|
||||
* new strings in the dictionary only for unmatched strings or for short
|
||||
* matches. It is used only for the fast compression options.
|
||||
*/
|
||||
local block_state deflate_fast(deflate_state *s, int flush)
|
||||
local block_state deflate_fast(s, flush)
|
||||
deflate_state *s;
|
||||
int flush;
|
||||
{
|
||||
IPos hash_head; /* head of the hash chain */
|
||||
int bflush; /* set if current block must be flushed */
|
||||
@@ -1726,7 +1782,9 @@ local block_state deflate_fast(deflate_state *s, int flush)
|
||||
* evaluation for matches: a match is finally adopted only if there is
|
||||
* no better match at the next window position.
|
||||
*/
|
||||
local block_state deflate_slow(deflate_state *s, int flush)
|
||||
local block_state deflate_slow(s, flush)
|
||||
deflate_state *s;
|
||||
int flush;
|
||||
{
|
||||
IPos hash_head; /* head of hash chain */
|
||||
int bflush; /* set if current block must be flushed */
|
||||
@@ -1865,7 +1923,9 @@ local block_state deflate_slow(deflate_state *s, int flush)
|
||||
* one. Do not maintain a hash table. (It will be regenerated if this run of
|
||||
* deflate switches away from Z_RLE.)
|
||||
*/
|
||||
local block_state deflate_rle(deflate_state *s, int flush)
|
||||
local block_state deflate_rle(s, flush)
|
||||
deflate_state *s;
|
||||
int flush;
|
||||
{
|
||||
int bflush; /* set if current block must be flushed */
|
||||
uInt prev; /* byte at distance one to match */
|
||||
@@ -1936,7 +1996,9 @@ local block_state deflate_rle(deflate_state *s, int flush)
|
||||
* For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
|
||||
* (It will be regenerated if this run of deflate switches away from Huffman.)
|
||||
*/
|
||||
local block_state deflate_huff(deflate_state *s, int flush)
|
||||
local block_state deflate_huff(s, flush)
|
||||
deflate_state *s;
|
||||
int flush;
|
||||
{
|
||||
int bflush; /* set if current block must be flushed */
|
||||
|
||||
|
||||
@@ -45,7 +45,9 @@
|
||||
requires strm->avail_out >= 258 for each loop to avoid checking for
|
||||
output space.
|
||||
*/
|
||||
void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start)
|
||||
void ZLIB_INTERNAL inflate_fast(strm, start)
|
||||
z_streamp strm;
|
||||
unsigned start; /* inflate()'s starting value for strm->avail_out */
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
z_const unsigned char FAR *in; /* local strm->next_in */
|
||||
|
||||
@@ -101,7 +101,8 @@ local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
|
||||
local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
|
||||
unsigned len));
|
||||
|
||||
int ZEXPORT inflateResetKeep(z_streamp strm)
|
||||
int ZEXPORT inflateResetKeep(strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
@@ -125,7 +126,8 @@ int ZEXPORT inflateResetKeep(z_streamp strm)
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateReset(z_streamp strm)
|
||||
int ZEXPORT inflateReset(strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
@@ -137,7 +139,9 @@ int ZEXPORT inflateReset(z_streamp strm)
|
||||
return inflateResetKeep(strm);
|
||||
}
|
||||
|
||||
int ZEXPORT inflateReset2(z_streamp strm, int windowBits)
|
||||
int ZEXPORT inflateReset2(strm, windowBits)
|
||||
z_streamp strm;
|
||||
int windowBits;
|
||||
{
|
||||
int wrap;
|
||||
struct inflate_state FAR *state;
|
||||
@@ -173,7 +177,11 @@ int ZEXPORT inflateReset2(z_streamp strm, int windowBits)
|
||||
return inflateReset(strm);
|
||||
}
|
||||
|
||||
int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, const char *version, int stream_size)
|
||||
int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
|
||||
z_streamp strm;
|
||||
int windowBits;
|
||||
const char *version;
|
||||
int stream_size;
|
||||
{
|
||||
int ret;
|
||||
struct inflate_state FAR *state;
|
||||
@@ -211,12 +219,18 @@ int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, const char *version, i
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateInit_(z_streamp strm, const char *version, int stream_size)
|
||||
int ZEXPORT inflateInit_(strm, version, stream_size)
|
||||
z_streamp strm;
|
||||
const char *version;
|
||||
int stream_size;
|
||||
{
|
||||
return inflateInit2_(strm, DEF_WBITS, version, stream_size);
|
||||
}
|
||||
|
||||
int ZEXPORT inflatePrime(z_streamp strm, int bits, int value)
|
||||
int ZEXPORT inflatePrime(strm, bits, value)
|
||||
z_streamp strm;
|
||||
int bits;
|
||||
int value;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
@@ -244,7 +258,8 @@ int ZEXPORT inflatePrime(z_streamp strm, int bits, int value)
|
||||
used for threaded applications, since the rewriting of the tables and virgin
|
||||
may not be thread-safe.
|
||||
*/
|
||||
local void fixedtables(struct inflate_state FAR *state)
|
||||
local void fixedtables(state)
|
||||
struct inflate_state FAR *state;
|
||||
{
|
||||
#ifdef BUILDFIXED
|
||||
static int virgin = 1;
|
||||
@@ -361,7 +376,10 @@ void makefixed()
|
||||
output will fall in the output data, making match copies simpler and faster.
|
||||
The advantage may be dependent on the size of the processor's data caches.
|
||||
*/
|
||||
local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy)
|
||||
local int updatewindow(strm, end, copy)
|
||||
z_streamp strm;
|
||||
const Bytef *end;
|
||||
unsigned copy;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
unsigned dist;
|
||||
@@ -584,7 +602,9 @@ local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy)
|
||||
will return Z_BUF_ERROR if it has not reached the end of the stream.
|
||||
*/
|
||||
|
||||
int ZEXPORT inflate(z_streamp strm, int flush)
|
||||
int ZEXPORT inflate(strm, flush)
|
||||
z_streamp strm;
|
||||
int flush;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
z_const unsigned char FAR *next; /* next input */
|
||||
@@ -1254,7 +1274,8 @@ int ZEXPORT inflate(z_streamp strm, int flush)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateEnd(z_streamp strm)
|
||||
int ZEXPORT inflateEnd(strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
|
||||
@@ -1267,7 +1288,10 @@ int ZEXPORT inflateEnd(z_streamp strm)
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLength)
|
||||
int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
|
||||
z_streamp strm;
|
||||
Bytef *dictionary;
|
||||
uInt *dictLength;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
@@ -1287,7 +1311,10 @@ int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLe
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt dictLength)
|
||||
int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
|
||||
z_streamp strm;
|
||||
const Bytef *dictionary;
|
||||
uInt dictLength;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
unsigned long dictid;
|
||||
@@ -1319,7 +1346,9 @@ int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt d
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head)
|
||||
int ZEXPORT inflateGetHeader(strm, head)
|
||||
z_streamp strm;
|
||||
gz_headerp head;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
@@ -1345,7 +1374,10 @@ int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head)
|
||||
called again with more data and the *have state. *have is initialized to
|
||||
zero for the first call.
|
||||
*/
|
||||
local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, unsigned len)
|
||||
local unsigned syncsearch(have, buf, len)
|
||||
unsigned FAR *have;
|
||||
const unsigned char FAR *buf;
|
||||
unsigned len;
|
||||
{
|
||||
unsigned got;
|
||||
unsigned next;
|
||||
@@ -1365,7 +1397,8 @@ local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, unsi
|
||||
return next;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateSync(z_streamp strm)
|
||||
int ZEXPORT inflateSync(strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
unsigned len; /* number of bytes to look at or looked at */
|
||||
unsigned long in, out; /* temporary to save total_in and total_out */
|
||||
@@ -1415,7 +1448,8 @@ int ZEXPORT inflateSync(z_streamp strm)
|
||||
block. When decompressing, PPP checks that at the end of input packet,
|
||||
inflate is waiting for these length bytes.
|
||||
*/
|
||||
int ZEXPORT inflateSyncPoint(z_streamp strm)
|
||||
int ZEXPORT inflateSyncPoint(strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
@@ -1424,7 +1458,9 @@ int ZEXPORT inflateSyncPoint(z_streamp strm)
|
||||
return state->mode == STORED && state->bits == 0;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateCopy(z_streamp dest, z_streamp source)
|
||||
int ZEXPORT inflateCopy(dest, source)
|
||||
z_streamp dest;
|
||||
z_streamp source;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
struct inflate_state FAR *copy;
|
||||
@@ -1469,7 +1505,9 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source)
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
int ZEXPORT inflateUndermine(z_streamp strm, int subvert)
|
||||
int ZEXPORT inflateUndermine(strm, subvert)
|
||||
z_streamp strm;
|
||||
int subvert;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
@@ -1484,7 +1522,8 @@ int ZEXPORT inflateUndermine(z_streamp strm, int subvert)
|
||||
#endif
|
||||
}
|
||||
|
||||
long ZEXPORT inflateMark(z_streamp strm)
|
||||
long ZEXPORT inflateMark(strm)
|
||||
z_streamp strm;
|
||||
{
|
||||
struct inflate_state FAR *state;
|
||||
|
||||
|
||||
@@ -29,9 +29,13 @@ const char inflate_copyright[] =
|
||||
table index bits. It will differ if the request is greater than the
|
||||
longest code or if it is less than the shortest code.
|
||||
*/
|
||||
int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
|
||||
unsigned codes, code FAR * FAR *table, unsigned FAR *bits,
|
||||
unsigned short FAR *work)
|
||||
int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)
|
||||
codetype type;
|
||||
unsigned short FAR *lens;
|
||||
unsigned codes;
|
||||
code FAR * FAR *table;
|
||||
unsigned FAR *bits;
|
||||
unsigned short FAR *work;
|
||||
{
|
||||
unsigned len; /* a code's length in bits */
|
||||
unsigned sym; /* index of code symbols */
|
||||
|
||||
97
zlib/trees.c
97
zlib/trees.c
@@ -185,7 +185,10 @@ local void gen_trees_header OF((void));
|
||||
#ifdef DEBUG
|
||||
local void send_bits OF((deflate_state *s, int value, int length));
|
||||
|
||||
local void send_bits(deflate_state *s, int value, int length)
|
||||
local void send_bits(s, value, length)
|
||||
deflate_state *s;
|
||||
int value; /* value to send */
|
||||
int length; /* number of bits */
|
||||
{
|
||||
Tracevv((stderr," l %2d v %4x ", length, value));
|
||||
Assert(length > 0 && length <= 15, "invalid length");
|
||||
@@ -228,7 +231,7 @@ local void send_bits(deflate_state *s, int value, int length)
|
||||
/* ===========================================================================
|
||||
* Initialize the various 'constant' tables.
|
||||
*/
|
||||
local void tr_static_init(void)
|
||||
local void tr_static_init()
|
||||
{
|
||||
#if defined(GEN_TREES_H) || !defined(STDC)
|
||||
static int static_init_done = 0;
|
||||
@@ -322,7 +325,7 @@ local void tr_static_init(void)
|
||||
((i) == (last)? "\n};\n\n" : \
|
||||
((i) % (width) == (width)-1 ? ",\n" : ", "))
|
||||
|
||||
void gen_trees_header(void)
|
||||
void gen_trees_header()
|
||||
{
|
||||
FILE *header = fopen("trees.h", "w");
|
||||
int i;
|
||||
@@ -375,7 +378,8 @@ void gen_trees_header(void)
|
||||
/* ===========================================================================
|
||||
* Initialize the tree data structures for a new zlib stream.
|
||||
*/
|
||||
void ZLIB_INTERNAL _tr_init(deflate_state *s)
|
||||
void ZLIB_INTERNAL _tr_init(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
tr_static_init();
|
||||
|
||||
@@ -402,7 +406,8 @@ void ZLIB_INTERNAL _tr_init(deflate_state *s)
|
||||
/* ===========================================================================
|
||||
* Initialize a new block.
|
||||
*/
|
||||
local void init_block(deflate_state *s)
|
||||
local void init_block(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
int n; /* iterates over tree elements */
|
||||
|
||||
@@ -445,7 +450,10 @@ local void init_block(deflate_state *s)
|
||||
* when the heap property is re-established (each father smaller than its
|
||||
* two sons).
|
||||
*/
|
||||
local void pqdownheap(deflate_state *s, ct_data *tree, int k)
|
||||
local void pqdownheap(s, tree, k)
|
||||
deflate_state *s;
|
||||
ct_data *tree; /* the tree to restore */
|
||||
int k; /* node to move down */
|
||||
{
|
||||
int v = s->heap[k];
|
||||
int j = k << 1; /* left son of k */
|
||||
@@ -477,7 +485,9 @@ local void pqdownheap(deflate_state *s, ct_data *tree, int k)
|
||||
* The length opt_len is updated; static_len is also updated if stree is
|
||||
* not null.
|
||||
*/
|
||||
local void gen_bitlen(deflate_state *s, tree_desc *desc)
|
||||
local void gen_bitlen(s, desc)
|
||||
deflate_state *s;
|
||||
tree_desc *desc; /* the tree descriptor */
|
||||
{
|
||||
ct_data *tree = desc->dyn_tree;
|
||||
int max_code = desc->max_code;
|
||||
@@ -562,7 +572,10 @@ local void gen_bitlen(deflate_state *s, tree_desc *desc)
|
||||
* OUT assertion: the field code is set for all tree elements of non
|
||||
* zero code length.
|
||||
*/
|
||||
local void gen_codes(ct_data *tree, int max_code, ushf *bl_count)
|
||||
local void gen_codes (tree, max_code, bl_count)
|
||||
ct_data *tree; /* the tree to decorate */
|
||||
int max_code; /* largest code with non zero frequency */
|
||||
ushf *bl_count; /* number of codes at each bit length */
|
||||
{
|
||||
ush next_code[MAX_BITS+1]; /* next code value for each bit length */
|
||||
ush code = 0; /* running code value */
|
||||
@@ -601,7 +614,9 @@ local void gen_codes(ct_data *tree, int max_code, ushf *bl_count)
|
||||
* and corresponding code. The length opt_len is updated; static_len is
|
||||
* also updated if stree is not null. The field max_code is set.
|
||||
*/
|
||||
local void build_tree(deflate_state *s, tree_desc *desc)
|
||||
local void build_tree(s, desc)
|
||||
deflate_state *s;
|
||||
tree_desc *desc; /* the tree descriptor */
|
||||
{
|
||||
ct_data *tree = desc->dyn_tree;
|
||||
const ct_data *stree = desc->stat_desc->static_tree;
|
||||
@@ -687,7 +702,10 @@ local void build_tree(deflate_state *s, tree_desc *desc)
|
||||
* Scan a literal or distance tree to determine the frequencies of the codes
|
||||
* in the bit length tree.
|
||||
*/
|
||||
local void scan_tree(deflate_state *s, ct_data *tree, int max_code)
|
||||
local void scan_tree (s, tree, max_code)
|
||||
deflate_state *s;
|
||||
ct_data *tree; /* the tree to be scanned */
|
||||
int max_code; /* and its largest code of non zero frequency */
|
||||
{
|
||||
int n; /* iterates over all tree elements */
|
||||
int prevlen = -1; /* last emitted length */
|
||||
@@ -729,7 +747,10 @@ local void scan_tree(deflate_state *s, ct_data *tree, int max_code)
|
||||
* Send a literal or distance tree in compressed form, using the codes in
|
||||
* bl_tree.
|
||||
*/
|
||||
local void send_tree(deflate_state *s, ct_data *tree, int max_code)
|
||||
local void send_tree (s, tree, max_code)
|
||||
deflate_state *s;
|
||||
ct_data *tree; /* the tree to be scanned */
|
||||
int max_code; /* and its largest code of non zero frequency */
|
||||
{
|
||||
int n; /* iterates over all tree elements */
|
||||
int prevlen = -1; /* last emitted length */
|
||||
@@ -777,7 +798,8 @@ local void send_tree(deflate_state *s, ct_data *tree, int max_code)
|
||||
* Construct the Huffman tree for the bit lengths and return the index in
|
||||
* bl_order of the last bit length code to send.
|
||||
*/
|
||||
local int build_bl_tree(deflate_state *s)
|
||||
local int build_bl_tree(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
int max_blindex; /* index of last bit length code of non zero freq */
|
||||
|
||||
@@ -811,7 +833,9 @@ local int build_bl_tree(deflate_state *s)
|
||||
* lengths of the bit length codes, the literal tree and the distance tree.
|
||||
* IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
|
||||
*/
|
||||
local void send_all_trees(deflate_state *s, int lcodes, int dcodes, int blcodes)
|
||||
local void send_all_trees(s, lcodes, dcodes, blcodes)
|
||||
deflate_state *s;
|
||||
int lcodes, dcodes, blcodes; /* number of codes for each tree */
|
||||
{
|
||||
int rank; /* index in bl_order */
|
||||
|
||||
@@ -838,7 +862,11 @@ local void send_all_trees(deflate_state *s, int lcodes, int dcodes, int blcodes)
|
||||
/* ===========================================================================
|
||||
* Send a stored block
|
||||
*/
|
||||
void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, ulg stored_len, int last)
|
||||
void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
|
||||
deflate_state *s;
|
||||
charf *buf; /* input block */
|
||||
ulg stored_len; /* length of input block */
|
||||
int last; /* one if this is the last block for a file */
|
||||
{
|
||||
send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */
|
||||
#ifdef DEBUG
|
||||
@@ -851,7 +879,8 @@ void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, ulg stored_len
|
||||
/* ===========================================================================
|
||||
* Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
|
||||
*/
|
||||
void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s)
|
||||
void ZLIB_INTERNAL _tr_flush_bits(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
bi_flush(s);
|
||||
}
|
||||
@@ -860,7 +889,8 @@ void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s)
|
||||
* Send one empty static block to give enough lookahead for inflate.
|
||||
* This takes 10 bits, of which 7 may remain in the bit buffer.
|
||||
*/
|
||||
void ZLIB_INTERNAL _tr_align(deflate_state *s)
|
||||
void ZLIB_INTERNAL _tr_align(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
send_bits(s, STATIC_TREES<<1, 3);
|
||||
send_code(s, END_BLOCK, static_ltree);
|
||||
@@ -874,7 +904,11 @@ void ZLIB_INTERNAL _tr_align(deflate_state *s)
|
||||
* Determine the best encoding for the current block: dynamic trees, static
|
||||
* trees or store, and output the encoded block to the zip file.
|
||||
*/
|
||||
void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, ulg stored_len, int last)
|
||||
void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
|
||||
deflate_state *s;
|
||||
charf *buf; /* input block, or NULL if too old */
|
||||
ulg stored_len; /* length of input block */
|
||||
int last; /* one if this is the last block for a file */
|
||||
{
|
||||
ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
|
||||
int max_blindex = 0; /* index of last bit length code of non zero freq */
|
||||
@@ -973,7 +1007,10 @@ void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, ulg stored_len,
|
||||
* Save the match info and tally the frequency counts. Return true if
|
||||
* the current block must be flushed.
|
||||
*/
|
||||
int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc)
|
||||
int ZLIB_INTERNAL _tr_tally (s, dist, lc)
|
||||
deflate_state *s;
|
||||
unsigned dist; /* distance of matched string */
|
||||
unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
|
||||
{
|
||||
s->sym_buf[s->sym_next++] = dist;
|
||||
s->sym_buf[s->sym_next++] = dist >> 8;
|
||||
@@ -998,7 +1035,10 @@ int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc)
|
||||
/* ===========================================================================
|
||||
* Send the block data compressed using the given Huffman trees
|
||||
*/
|
||||
local void compress_block(deflate_state *s, const ct_data *ltree, const ct_data *dtree)
|
||||
local void compress_block(s, ltree, dtree)
|
||||
deflate_state *s;
|
||||
const ct_data *ltree; /* literal tree */
|
||||
const ct_data *dtree; /* distance tree */
|
||||
{
|
||||
unsigned dist; /* distance of matched string */
|
||||
int lc; /* match length or unmatched char (if dist == 0) */
|
||||
@@ -1055,7 +1095,8 @@ local void compress_block(deflate_state *s, const ct_data *ltree, const ct_data
|
||||
* (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
|
||||
* IN assertion: the fields Freq of dyn_ltree are set.
|
||||
*/
|
||||
local int detect_data_type(deflate_state *s)
|
||||
local int detect_data_type(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
/* black_mask is the bit mask of black-listed bytes
|
||||
* set bits 0..6, 14..25, and 28..31
|
||||
@@ -1088,7 +1129,9 @@ local int detect_data_type(deflate_state *s)
|
||||
* method would use a table)
|
||||
* IN assertion: 1 <= len <= 15
|
||||
*/
|
||||
local unsigned bi_reverse(unsigned code, int len)
|
||||
local unsigned bi_reverse(code, len)
|
||||
unsigned code; /* the value to invert */
|
||||
int len; /* its bit length */
|
||||
{
|
||||
register unsigned res = 0;
|
||||
do {
|
||||
@@ -1101,7 +1144,8 @@ local unsigned bi_reverse(unsigned code, int len)
|
||||
/* ===========================================================================
|
||||
* Flush the bit buffer, keeping at most 7 bits in it.
|
||||
*/
|
||||
local void bi_flush(deflate_state *s)
|
||||
local void bi_flush(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
if (s->bi_valid == 16) {
|
||||
put_short(s, s->bi_buf);
|
||||
@@ -1117,7 +1161,8 @@ local void bi_flush(deflate_state *s)
|
||||
/* ===========================================================================
|
||||
* Flush the bit buffer and align the output on a byte boundary
|
||||
*/
|
||||
local void bi_windup(deflate_state *s)
|
||||
local void bi_windup(s)
|
||||
deflate_state *s;
|
||||
{
|
||||
if (s->bi_valid > 8) {
|
||||
put_short(s, s->bi_buf);
|
||||
@@ -1135,7 +1180,11 @@ local void bi_windup(deflate_state *s)
|
||||
* Copy a stored block, storing first the length and its
|
||||
* one's complement if requested.
|
||||
*/
|
||||
local void copy_block(deflate_state *s, charf *buf, unsigned len, int header)
|
||||
local void copy_block(s, buf, len, header)
|
||||
deflate_state *s;
|
||||
charf *buf; /* the input data */
|
||||
unsigned len; /* its length */
|
||||
int header; /* true if block header must be written */
|
||||
{
|
||||
bi_windup(s); /* align on byte boundary */
|
||||
|
||||
|
||||
33
zlib/zutil.c
33
zlib/zutil.c
@@ -27,12 +27,12 @@ z_const char * const z_errmsg[10] = {
|
||||
""};
|
||||
|
||||
|
||||
const char * ZEXPORT zlibVersion(void)
|
||||
const char * ZEXPORT zlibVersion()
|
||||
{
|
||||
return ZLIB_VERSION;
|
||||
}
|
||||
|
||||
uLong ZEXPORT zlibCompileFlags(void)
|
||||
uLong ZEXPORT zlibCompileFlags()
|
||||
{
|
||||
uLong flags;
|
||||
|
||||
@@ -122,7 +122,8 @@ uLong ZEXPORT zlibCompileFlags(void)
|
||||
# endif
|
||||
int ZLIB_INTERNAL z_verbose = verbose;
|
||||
|
||||
void ZLIB_INTERNAL z_error (char *m)
|
||||
void ZLIB_INTERNAL z_error (m)
|
||||
char *m;
|
||||
{
|
||||
fprintf(stderr, "%s\n", m);
|
||||
exit(1);
|
||||
@@ -132,7 +133,8 @@ void ZLIB_INTERNAL z_error (char *m)
|
||||
/* exported to allow conversion of error code to string for compress() and
|
||||
* uncompress()
|
||||
*/
|
||||
const char * ZEXPORT zError(int err)
|
||||
const char * ZEXPORT zError(err)
|
||||
int err;
|
||||
{
|
||||
return ERR_MSG(err);
|
||||
}
|
||||
@@ -147,7 +149,10 @@ const char * ZEXPORT zError(int err)
|
||||
|
||||
#ifndef HAVE_MEMCPY
|
||||
|
||||
void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len)
|
||||
void ZLIB_INTERNAL zmemcpy(dest, source, len)
|
||||
Bytef* dest;
|
||||
const Bytef* source;
|
||||
uInt len;
|
||||
{
|
||||
if (len == 0) return;
|
||||
do {
|
||||
@@ -155,7 +160,10 @@ void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len)
|
||||
} while (--len != 0);
|
||||
}
|
||||
|
||||
int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len)
|
||||
int ZLIB_INTERNAL zmemcmp(s1, s2, len)
|
||||
const Bytef* s1;
|
||||
const Bytef* s2;
|
||||
uInt len;
|
||||
{
|
||||
uInt j;
|
||||
|
||||
@@ -165,7 +173,9 @@ int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len)
|
||||
void ZLIB_INTERNAL zmemzero(dest, len)
|
||||
Bytef* dest;
|
||||
uInt len;
|
||||
{
|
||||
if (len == 0) return;
|
||||
do {
|
||||
@@ -291,14 +301,19 @@ extern voidp calloc OF((uInt items, uInt size));
|
||||
extern void free OF((voidpf ptr));
|
||||
#endif
|
||||
|
||||
voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
|
||||
voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
|
||||
voidpf opaque;
|
||||
unsigned items;
|
||||
unsigned size;
|
||||
{
|
||||
if (opaque) items += size - size; /* make compiler happy */
|
||||
return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
|
||||
(voidpf)calloc(items, size);
|
||||
}
|
||||
|
||||
void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
|
||||
void ZLIB_INTERNAL zcfree (opaque, ptr)
|
||||
voidpf opaque;
|
||||
voidpf ptr;
|
||||
{
|
||||
free(ptr);
|
||||
if (opaque) return; /* make compiler happy */
|
||||
|
||||
Reference in New Issue
Block a user