mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-05-25 15:25:30 -04:00
Compare commits
194 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f5a5857ce | ||
|
|
bc63ea82f2 | ||
|
|
0d4fb1bc89 | ||
|
|
52480aaac2 | ||
|
|
702a8f61b7 | ||
|
|
2928b2742e | ||
|
|
f1d5a3c815 | ||
|
|
3086dbc0fd | ||
|
|
63e599b921 | ||
|
|
340238421d | ||
|
|
31fbb17d23 | ||
|
|
edf298ace5 | ||
|
|
1d5b5ab83a | ||
|
|
b0ba699031 | ||
|
|
e57c7f5d87 | ||
|
|
05f30c05c9 | ||
|
|
922681e140 | ||
|
|
273b9f265f | ||
|
|
0d546ee3b4 | ||
|
|
d6124a82a4 | ||
|
|
1d828f35ca | ||
|
|
7bba25e675 | ||
|
|
6e3140d5ba | ||
|
|
1d8f47cc71 | ||
|
|
743d715d43 | ||
|
|
4b862306e5 | ||
|
|
70948a9dc3 | ||
|
|
951bf0a446 | ||
|
|
bea8a3a16f | ||
|
|
bf8aab51e8 | ||
|
|
1f689ec0c2 | ||
|
|
8839314025 | ||
|
|
47e087d8eb | ||
|
|
e1c5f0e93a | ||
|
|
cfdc27c613 | ||
|
|
7e7372a0c5 | ||
|
|
8cad2097e9 | ||
|
|
d039cfa829 | ||
|
|
0af88421dc | ||
|
|
9d014670df | ||
|
|
647a00a278 | ||
|
|
2c7777aaa6 | ||
|
|
6af41d2357 | ||
|
|
a0b9a8e989 | ||
|
|
ac692b199c | ||
|
|
147e9bea8c | ||
|
|
a5fc5ebe7a | ||
|
|
c79cb81a4f | ||
|
|
650643109e | ||
|
|
4cf08983e8 | ||
|
|
8112445318 | ||
|
|
c38f20c5ff | ||
|
|
0cf200ecbb | ||
|
|
e4c681fefd | ||
|
|
c44c90e946 | ||
|
|
fc592a8e25 | ||
|
|
40a6e13071 | ||
|
|
3cc6a9e8cd | ||
|
|
30656c5e35 | ||
|
|
15d2964256 | ||
|
|
862fe4eeaf | ||
|
|
859d44fa4f | ||
|
|
f1c24ab03b | ||
|
|
b9cc0c6176 | ||
|
|
c60550bff9 | ||
|
|
67f1dcf604 | ||
|
|
79fd7d5885 | ||
|
|
dfdcd8f851 | ||
|
|
04e2fc2c76 | ||
|
|
7f60ec001a | ||
|
|
4fa7156ccd | ||
|
|
dcf364dac5 | ||
|
|
d1eff8f0dc | ||
|
|
8f727166d9 | ||
|
|
5bcb3deb2f | ||
|
|
de3cc03b03 | ||
|
|
006ee327d6 | ||
|
|
9b6363fa10 | ||
|
|
9e2f0fe9ae | ||
|
|
4f6e4ea64a | ||
|
|
567c40935f | ||
|
|
8e11f0c169 | ||
|
|
e9dbc8d66d | ||
|
|
bd2dbd2f32 | ||
|
|
350e295d1c | ||
|
|
066156fcd9 | ||
|
|
a5bbe859db | ||
|
|
d046525de3 | ||
|
|
bb0a8118c2 | ||
|
|
d1df0aaf70 | ||
|
|
15d8e49a64 | ||
|
|
b905ab23af | ||
|
|
aa142f08ef | ||
|
|
236417cf35 | ||
|
|
2a97d81e99 | ||
|
|
359e539a72 | ||
|
|
9e0898460d | ||
|
|
185520a141 | ||
|
|
c98f9d1f68 | ||
|
|
1f9ce2fcbe | ||
|
|
797e17fc4a | ||
|
|
c2db921890 | ||
|
|
77be09aaed | ||
|
|
0d0f615240 | ||
|
|
b6457bbc83 | ||
|
|
1807ce485a | ||
|
|
9c175ac9ef | ||
|
|
a84b79ea58 | ||
|
|
d4c4f6754e | ||
|
|
a4b926dcdc | ||
|
|
0973d0e380 | ||
|
|
e405cfc073 | ||
|
|
b78a841bb0 | ||
|
|
f7a2b8a3fa | ||
|
|
d941807915 | ||
|
|
992e10efaf | ||
|
|
1c5ebdc4e5 | ||
|
|
9994933c8c | ||
|
|
23d9ead5af | ||
|
|
fcfdd36054 | ||
|
|
89b847393f | ||
|
|
788ecbe5ea | ||
|
|
353506bc51 | ||
|
|
7cff121ec8 | ||
|
|
14f33837dc | ||
|
|
3305a7a063 | ||
|
|
494879b819 | ||
|
|
8d6da040e5 | ||
|
|
68e9add76a | ||
|
|
dc34990b2e | ||
|
|
81ead9e70c | ||
|
|
996af4a79f | ||
|
|
dacadd53a9 | ||
|
|
a6312e60c9 | ||
|
|
e3ee0e7319 | ||
|
|
0fd29b6bcb | ||
|
|
7f79682732 | ||
|
|
870b7d96dc | ||
|
|
9dc31473ba | ||
|
|
536ae3f4ef | ||
|
|
0590b09d9a | ||
|
|
407c71c7ce | ||
|
|
344327385f | ||
|
|
688f5c379a | ||
|
|
9f86ddc965 | ||
|
|
c35e28331f | ||
|
|
b4a27ca25d | ||
|
|
8ad4b5d912 | ||
|
|
589b0691e5 | ||
|
|
36212021f0 | ||
|
|
2b38542e0d | ||
|
|
321dd78f8c | ||
|
|
6f10f12577 | ||
|
|
1a95869dfc | ||
|
|
c9fe6ca304 | ||
|
|
990fa5c1e1 | ||
|
|
07069880a2 | ||
|
|
e55b190f4a | ||
|
|
48d51a1370 | ||
|
|
f654e47691 | ||
|
|
83ad3533d4 | ||
|
|
fa28c5d693 | ||
|
|
62bb9bba02 | ||
|
|
6601510425 | ||
|
|
f7ac7ffd16 | ||
|
|
4320c25fcc | ||
|
|
4490fb8660 | ||
|
|
475ca7d43c | ||
|
|
7c3c54b132 | ||
|
|
bcf0738f98 | ||
|
|
8749ec6436 | ||
|
|
42e2b56c4e | ||
|
|
0902b52f66 | ||
|
|
9615a2492b | ||
|
|
4592aa770d | ||
|
|
8bc363cc9f | ||
|
|
a9a3155756 | ||
|
|
fcc79836b8 | ||
|
|
804411b7fd | ||
|
|
0b1b2a3ff4 | ||
|
|
50bdf9685d | ||
|
|
3f2a38b011 | ||
|
|
5510255f12 | ||
|
|
56a039b04a | ||
|
|
7bc3be2b9e | ||
|
|
411c4789df | ||
|
|
231b239f30 | ||
|
|
4c8683c875 | ||
|
|
85c906f964 | ||
|
|
35f5a21a16 | ||
|
|
99673f937f | ||
|
|
9505ac5945 | ||
|
|
0dd25d4752 | ||
|
|
ae3e13ba99 |
23
.cirrus.yml
23
.cirrus.yml
@@ -1,23 +0,0 @@
|
||||
freebsd_task:
|
||||
name: FreeBSD
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-1
|
||||
env:
|
||||
PATH: /usr/local/bin:$PATH
|
||||
prep_script:
|
||||
- dd if=/dev/zero of=/tmp/zpool bs=1M count=1024
|
||||
- zpool create -m `pwd`/testtmp zpool /tmp/zpool
|
||||
- pkg install -y bash autotools m4 xxhash zstd liblz4 wget
|
||||
- wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
|
||||
configure_script:
|
||||
- CPPFLAGS=-I/usr/local/include/ LDFLAGS=-L/usr/local/lib/ ./configure --disable-md2man
|
||||
make_script:
|
||||
- make
|
||||
install_script:
|
||||
- make install
|
||||
info_script:
|
||||
- rsync --version
|
||||
test_script:
|
||||
- RSYNC_EXPECT_SKIPPED=acls-default,acls,crtimes,protected-regular make check
|
||||
ssl_file_list_script:
|
||||
- rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
7
.gitattributes
vendored
7
.gitattributes
vendored
@@ -1 +1,8 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
# The rsync-web/ subdirectory holds the project website source content
|
||||
# (mirrors what gets pushed to https://rsync.samba.org). Exclude it from
|
||||
# `git archive` output so the release source tarball produced by
|
||||
# packaging/release.py step_7_tarball does not bloat with HTML the
|
||||
# tarball doesn't need.
|
||||
/rsync-web/ export-ignore
|
||||
|
||||
82
.github/workflows/almalinux-8-build.yml
vendored
Normal file
82
.github/workflows/almalinux-8-build.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
name: Test rsync on AlmaLinux 8
|
||||
|
||||
# Older-LTS coverage on the Fedora/RHEL family to help with backporting
|
||||
# security fixes. AlmaLinux 8 is the RHEL 8 rebuild and is the oldest
|
||||
# active LTS in this family (RHEL 8 full support runs to 2029).
|
||||
# GitHub Actions has no native runner for this family, so the job runs
|
||||
# inside an almalinux:8 container hosted on ubuntu-latest.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: almalinux:8
|
||||
name: Test rsync on AlmaLinux 8
|
||||
steps:
|
||||
- name: install git
|
||||
# actions/checkout needs git in the container before the checkout step.
|
||||
run: dnf -y install git
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
# PowerTools is needed for libzstd-devel etc; xxhash and lz4 dev
|
||||
# headers live in EPEL on RHEL 8. The default python3 on RHEL 8
|
||||
# is 3.6, which is too old for runtests.py (uses capture_output=
|
||||
# / text= introduced in 3.7), so install python39 and point
|
||||
# /usr/bin/python3 at it.
|
||||
run: |
|
||||
dnf -y install epel-release
|
||||
dnf config-manager --set-enabled powertools
|
||||
dnf -y install gcc gcc-c++ make autoconf automake m4 \
|
||||
python39 python39-pip diffutils \
|
||||
openssl openssl-devel \
|
||||
attr libattr-devel acl libacl-devel \
|
||||
zstd libzstd-devel \
|
||||
lz4 lz4-devel \
|
||||
xxhash xxhash-devel
|
||||
alternatives --set python3 /usr/bin/python3.9
|
||||
pip3 install commonmark
|
||||
- name: configure
|
||||
run: ./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: info
|
||||
run: ./rsync --version
|
||||
- name: check
|
||||
# In the container we already run as root, so no sudo. The
|
||||
# crtimes-not-supported skip matches the other Linux jobs;
|
||||
# daemon-chroot-acl and proxy-response-line-too-long skip because
|
||||
# the default (secure) transport opens no listening socket.
|
||||
run: RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run exercising the real loopback-TCP daemon path.
|
||||
run: ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: ./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: almalinux-8-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
120
.github/workflows/android-static-build.yml
vendored
Normal file
120
.github/workflows/android-static-build.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
name: Build static rsync for Android
|
||||
|
||||
# Cross-compiles statically-linked rsync binaries with the Android NDK,
|
||||
# suitable for dropping onto a phone (adb push / Termux) with no shared
|
||||
# libraries. arm64-v8a covers all modern phones; armeabi-v7a covers older
|
||||
# 32-bit devices. The binaries are uploaded as workflow artifacts.
|
||||
#
|
||||
# These are cross-compiled, so the test suite can't run here; we sanity
|
||||
# check that each binary is the right architecture, is static, and that
|
||||
# it executes (`--version`) under qemu-user.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/android-static-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/android-static-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
# Minimum supported API level. 24 (Android 7.0) runs on every modern
|
||||
# phone while keeping broad reach; bump if you need newer Bionic APIs.
|
||||
ANDROID_API: 24
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.abi }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- abi: arm64-v8a # modern phones
|
||||
triple: aarch64-linux-android
|
||||
qemu: qemu-aarch64-static
|
||||
- abi: armeabi-v7a # older 32-bit phones
|
||||
triple: armv7a-linux-androideabi
|
||||
qemu: qemu-arm-static
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install build prerequisites
|
||||
run: sudo apt-get update && sudo apt-get install -y autoconf automake gawk qemu-user-static
|
||||
|
||||
- name: Configure and build (${{ matrix.abi }})
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
NDK="${ANDROID_NDK_LATEST_HOME:-$ANDROID_NDK_ROOT}"
|
||||
TC="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
export CC="$TC/${{ matrix.triple }}${ANDROID_API}-clang"
|
||||
export AR="$TC/llvm-ar" RANLIB="$TC/llvm-ranlib" STRIP="$TC/llvm-strip"
|
||||
export CFLAGS="-O2" LDFLAGS="-static"
|
||||
|
||||
# Bionic doesn't declare lchmod()/lutimes() until API 36, but the
|
||||
# symbols link, so configure mis-detects them -- force them off so
|
||||
# rsync uses its fallbacks. The other cache vars restore values
|
||||
# that configure can't probe when cross-compiling (Android runs a
|
||||
# normal Linux kernel, so these match the native Linux result).
|
||||
export ac_cv_func_lchmod=no ac_cv_func_lutimes=no \
|
||||
rsync_cv_HAVE_SOCKETPAIR=yes \
|
||||
rsync_cv_MKNOD_CREATES_FIFOS=yes \
|
||||
rsync_cv_MKNOD_CREATES_SOCKETS=yes
|
||||
|
||||
# Self-contained build: drop optional external libraries so the
|
||||
# static binary needs nothing at runtime. rsync keeps md5/md4
|
||||
# checksums and its bundled zlib.
|
||||
./configure --host=${{ matrix.triple }} --build=x86_64-pc-linux-gnu \
|
||||
--enable-ipv6 \
|
||||
--disable-zstd --disable-lz4 --disable-xxhash --disable-openssl \
|
||||
--disable-iconv --disable-iconv-open \
|
||||
--disable-acl-support --disable-xattr-support \
|
||||
--disable-md2man --disable-roll-simd \
|
||||
--with-included-popt --with-included-zlib
|
||||
|
||||
# Generate the awk-built headers serially first so the parallel
|
||||
# build can't race on proto.h <- daemon-parm.h.
|
||||
make proto.h
|
||||
make -j"$(nproc)" rsync
|
||||
"$STRIP" rsync
|
||||
|
||||
- name: Verify binary
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
file rsync
|
||||
# Gate: must be a statically-linked executable (no interpreter).
|
||||
file rsync | grep -q "statically linked"
|
||||
if file rsync | grep -q "dynamically linked"; then
|
||||
echo "ERROR: binary is not static" >&2; exit 1
|
||||
fi
|
||||
# Best-effort: confirm it actually runs under qemu-user.
|
||||
${{ matrix.qemu }} ./rsync --version | head -3 || \
|
||||
echo "WARNING: qemu smoke test did not run cleanly (check on a real device)"
|
||||
|
||||
- name: Package
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VER=$(sed -n 's/.*RSYNC_VERSION "\([^"]*\)".*/\1/p' version.h)
|
||||
out="rsync-${VER}-android-${{ matrix.abi }}"
|
||||
mkdir -p dist
|
||||
cp rsync "dist/$out"
|
||||
( cd dist && sha256sum "$out" > "$out.sha256" )
|
||||
echo "ARTIFACT_NAME=rsync-android-${{ matrix.abi }}" >>"$GITHUB_ENV"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: dist/
|
||||
125
.github/workflows/build.yml
vendored
125
.github/workflows/build.yml
vendored
@@ -1,125 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore: [ .cirrus.yml ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore: [ .cirrus.yml ]
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@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
|
||||
71
.github/workflows/coverage.yml
vendored
Normal file
71
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: Coverage (Ubuntu)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/coverage.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/coverage.yml'
|
||||
schedule:
|
||||
- cron: '42 9 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
name: gcov coverage
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl gcovr
|
||||
echo "/usr/local/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: ./configure --enable-coverage --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: info
|
||||
run: rsync --version
|
||||
# Two coverage runs: the default pipe transport, then a second pass over a
|
||||
# real loopback rsyncd (--use-tcp) which also exercises the require_tcp-only
|
||||
# tests. gcovr's --print-summary line/branch/decision totals go to the step
|
||||
# log (and the job summary below), so the numbers are visible in CI.
|
||||
# `make coverage` exits with the suite's status, so a regression fails CI.
|
||||
- name: coverage (pipe transport)
|
||||
run: |
|
||||
set -o pipefail
|
||||
sudo make coverage 2>&1 | tee cov-pipe.log
|
||||
- name: coverage (TCP transport)
|
||||
run: |
|
||||
set -o pipefail
|
||||
sudo make coverage-tcp 2>&1 | tee cov-tcp.log
|
||||
- name: coverage summary
|
||||
if: always()
|
||||
run: |
|
||||
{
|
||||
echo "## gcov coverage"
|
||||
echo "### Pipe transport (\`make coverage\`)"
|
||||
echo '```'
|
||||
grep -E '^(lines|functions|branches|decisions):' cov-pipe.log || echo '(no summary -- see step log)'
|
||||
echo '```'
|
||||
echo "### TCP transport (\`make coverage-tcp\`)"
|
||||
echo '```'
|
||||
grep -E '^(lines|functions|branches|decisions):' cov-tcp.log || echo '(no summary -- see step log)'
|
||||
echo '```'
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
- name: upload HTML reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-html
|
||||
path: |
|
||||
coverage
|
||||
coverage-tcp
|
||||
65
.github/workflows/cygwin-build.yml
vendored
Normal file
65
.github/workflows/cygwin-build.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Test rsync on Cygwin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: windows-2022
|
||||
name: Test rsync on Cygwin
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: cygwin
|
||||
run: choco install -y --no-progress cygwin cyg-get
|
||||
- name: prep
|
||||
run: |
|
||||
cyg-get make autoconf automake gcc-core attr libattr-devel python39 python39-pip libzstd-devel liblz4-devel libssl-devel libxxhash0 libxxhash-devel
|
||||
echo "C:/tools/cygwin/bin" >>$Env:GITHUB_PATH
|
||||
- name: commonmark
|
||||
run: bash -c 'python3 -mpip install --user commonmark'
|
||||
- name: configure
|
||||
run: bash -c './configure --with-rrsync'
|
||||
- name: make
|
||||
run: bash -c 'make'
|
||||
- name: install
|
||||
run: bash -c 'make install'
|
||||
- name: info
|
||||
run: bash -c '/usr/local/bin/rsync --version'
|
||||
- name: check
|
||||
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on Cygwin
|
||||
# (rsyncfns.py drives xattrs via getfattr/setfattr from the `attr`
|
||||
# package installed above), verified on a real Cygwin host. The real
|
||||
# chown/devices tests still skip (need root/mknod), as do the
|
||||
# RESOLVE_BENEATH symlink-race tests.
|
||||
run: bash -c 'RSYNC_EXPECT_SKIPPED=acls-default,acls-depth,acls,bare-do-open-symlink-race,chdir-symlink-race,chown,daemon-access-ip,daemon-chroot-acl,devices,dir-sgid,open-noatime,protected-regular,proxy-response-line-too-long,sender-flist-symlink-leak,simd-checksum,symlink-dirlink-basis make check'
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd; the default
|
||||
# 'make check' above uses the secure stdio-pipe transport.
|
||||
run: bash -c './runtests.py --rsync-bin=`pwd`/rsync.exe --use-tcp -j 8'
|
||||
- name: ssl file list
|
||||
run: bash -c 'PATH="/usr/local/bin:$PATH" rsync-ssl --no-motd download.samba.org::rsyncftp/ || true'
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cygwin-bin
|
||||
path: |
|
||||
rsync.exe
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
51
.github/workflows/freebsd-build.yml
vendored
Normal file
51
.github/workflows/freebsd-build.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Test rsync on FreeBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on FreeBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in FreeBSD VM
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y bash autotools m4 devel/xxhash zstd liblz4 python3 archivers/liblz4 git
|
||||
run: |
|
||||
freebsd-version
|
||||
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: freebsd-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
65
.github/workflows/macos-build.yml
vendored
Normal file
65
.github/workflows/macos-build.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Test rsync on macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
name: Test rsync on macOS
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
brew install automake openssl xxhash zstd lz4
|
||||
pip3 install --user --break-system-packages commonmark
|
||||
echo "$(brew --prefix)/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: |
|
||||
BREW_PREFIX=$(brew --prefix)
|
||||
OPENSSL_PREFIX=$(brew --prefix openssl)
|
||||
CPPFLAGS="-I${BREW_PREFIX}/include -I${OPENSSL_PREFIX}/include" \
|
||||
LDFLAGS="-L${BREW_PREFIX}/lib -L${OPENSSL_PREFIX}/lib" \
|
||||
./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: info
|
||||
run: rsync --version
|
||||
- name: check
|
||||
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on macOS
|
||||
# (rsyncfns.py drives xattrs via the `xattr` command), verified on a
|
||||
# real macOS host, so they're no longer in the skip set.
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=acls-default,acls-depth,chmod-temp-dir,daemon-access-ip,daemon-chroot-acl,dir-sgid,open-noatime,preallocate,protected-regular,proxy-response-line-too-long,simd-checksum,sparse make check
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd; the default
|
||||
# 'make check' above uses the secure stdio-pipe transport.
|
||||
run: sudo ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
52
.github/workflows/netbsd-build.yml
vendored
Normal file
52
.github/workflows/netbsd-build.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Test rsync on NetBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on NetBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in NetBSD VM
|
||||
id: test
|
||||
uses: vmactions/netbsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
PATH=/usr/sbin:$PATH pkg_add autoconf automake python312
|
||||
ln -sf /usr/pkg/bin/python3.12 /usr/pkg/bin/python3
|
||||
run: |
|
||||
uname -a
|
||||
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: netbsd-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
61
.github/workflows/openbsd-build.yml
vendored
Normal file
61
.github/workflows/openbsd-build.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: Test rsync on OpenBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on OpenBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in OpenBSD VM
|
||||
id: test
|
||||
uses: vmactions/openbsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg_add -I bash autoconf%2.71 automake%1.16
|
||||
run: |
|
||||
uname -a
|
||||
export AUTOCONF_VERSION=2.71
|
||||
export AUTOMAKE_VERSION=1.16
|
||||
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
# The --use-tcp daemon tests run at -j2 here (vs -j8 elsewhere): this
|
||||
# job runs inside a nested VM, and at -j8 the many concurrent loopback
|
||||
# daemons occasionally lose a connection-handshake timing race under
|
||||
# that resource pressure, hanging one test to the 300s timeout. It is
|
||||
# an environment artifact, not an rsync bug (the handshake is
|
||||
# deadlock-free and unreproducible elsewhere, even pinned to 1 CPU at
|
||||
# -j8); -j2 keeps the VM from over-subscribing. The pipe `make check`
|
||||
# above stays at the default parallelism.
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 2
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openbsd-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
51
.github/workflows/solaris-build.yml
vendored
Normal file
51
.github/workflows/solaris-build.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Test rsync on Solaris
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on Solaris
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Test in Solaris VM
|
||||
id: test
|
||||
uses: vmactions/solaris-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install bash automake gnu-m4 pkg://solaris/runtime/python-35 autoconf gcc git
|
||||
run: |
|
||||
uname -a
|
||||
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
|
||||
make
|
||||
./rsync --version
|
||||
make check
|
||||
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: solaris-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
64
.github/workflows/ubuntu-22.04-build.yml
vendored
Normal file
64
.github/workflows/ubuntu-22.04-build.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
|
||||
# Older-LTS coverage to help with backporting security fixes. ubuntu-22.04
|
||||
# is currently the oldest GitHub Actions runner image (20.04 was retired
|
||||
# in April 2025).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
|
||||
echo "/usr/local/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: ./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: info
|
||||
run: rsync --version
|
||||
- name: check
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check29
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd; the default
|
||||
# 'make check' above uses the secure stdio-pipe transport.
|
||||
run: sudo ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ubuntu-22.04-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
62
.github/workflows/ubuntu-build.yml
vendored
Normal file
62
.github/workflows/ubuntu-build.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Test rsync on Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on Ubuntu
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
|
||||
echo "/usr/local/bin" >>$GITHUB_PATH
|
||||
- name: configure
|
||||
run: ./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: info
|
||||
run: rsync --version
|
||||
- name: check
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long make check29
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd. The default
|
||||
# 'make check' above uses the secure stdio-pipe transport (no listening
|
||||
# sockets); this run exercises the real TCP accept/auth path. Skip-set
|
||||
# is env-dependent here (chroot-acl), so leave RSYNC_EXPECT_SKIPPED unset.
|
||||
run: sudo ./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ubuntu-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -58,3 +58,4 @@ aclocal.m4
|
||||
/auto-build-save
|
||||
.deps
|
||||
/*.exe
|
||||
*.dSYM/
|
||||
|
||||
21
INSTALL.md
21
INSTALL.md
@@ -7,6 +7,22 @@ option to use if you want to just skip that feature. What follows are various
|
||||
support libraries that you may want to install to build rsync with the maximum
|
||||
features (the impatient can skip down to the package summary):
|
||||
|
||||
## Ubuntu users: skip the build, use the PPA
|
||||
|
||||
If you are on a currently supported Ubuntu series (jammy 22.04 LTS, noble
|
||||
24.04 LTS, questing 25.10, resolute 26.04 LTS) and just want the latest
|
||||
upstream rsync, the rsync project maintains a Launchpad PPA that tracks
|
||||
stable releases:
|
||||
|
||||
> sudo add-apt-repository ppa:rsyncproject/rsync
|
||||
> sudo apt update && sudo apt install rsync
|
||||
|
||||
See [the PPA page][ppa] for current build status across architectures.
|
||||
|
||||
[ppa]: https://launchpad.net/~rsyncproject/+archive/ubuntu/rsync
|
||||
|
||||
The rest of this document covers building from source.
|
||||
|
||||
## The basic setup
|
||||
|
||||
You need to have a C compiler installed and optionally a C++ compiler in order
|
||||
@@ -104,6 +120,8 @@ like.
|
||||
> sudo apt install -y liblz4-dev
|
||||
> sudo apt install -y libssl-dev
|
||||
|
||||
Or run support/install_deps_ubuntu.sh
|
||||
|
||||
- For CentOS (use EPEL for python3-pip):
|
||||
|
||||
> sudo yum -y install epel-release
|
||||
@@ -230,6 +248,9 @@ not completely implement the "New Sockets" API.
|
||||
[This site][5] says that Apple started to support IPv6 in 10.2 (Jaguar). If
|
||||
your build fails, try again after running configure with `--disable-ipv6`.
|
||||
|
||||
Apple Silicon macs may install packages in a slightly different location and require flags.
|
||||
CFLAGS="-I /opt/homebrew/include" LDFLAGS="-L /opt/homebrew/lib"
|
||||
|
||||
[5]: http://www.ipv6.org/impl/mac.html
|
||||
|
||||
## IBM AIX notes
|
||||
|
||||
155
Makefile.in
155
Makefile.in
@@ -49,20 +49,22 @@ OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
|
||||
usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
|
||||
OBJS3=progress.o pipe.o @MD5_ASM@ @ROLL_SIMD@ @ROLL_ASM@
|
||||
DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
|
||||
popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
|
||||
popt/popthelp.o popt/poptparse.o
|
||||
popt_OBJS= popt/popt.o popt/poptconfig.o \
|
||||
popt/popthelp.o popt/poptparse.o popt/poptint.o
|
||||
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
|
||||
|
||||
TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
|
||||
|
||||
# Programs we must have to run the test cases
|
||||
CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
|
||||
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(EXEEXT)
|
||||
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) t_chmod_secure$(EXEEXT) \
|
||||
t_secure_relpath$(EXEEXT) wildtest$(EXEEXT) simdtest$(EXEEXT)
|
||||
|
||||
CHECK_SYMLINKS = testsuite/chown-fake.test testsuite/devices-fake.test testsuite/xattrs-hlink.test
|
||||
CHECK_SYMLINKS = testsuite/chown-fake_test.py testsuite/devices-fake_test.py \
|
||||
testsuite/xattrs-hlink_test.py testsuite/exclude-lsh_test.py
|
||||
|
||||
# Objects for CHECK_PROGS to clean
|
||||
CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest.o
|
||||
CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o t_chmod_secure.o t_secure_relpath.o trimslash.o wildtest.o
|
||||
|
||||
# note that the -I. is needed to handle config.h when using VPATH
|
||||
.c.o:
|
||||
@@ -178,20 +180,20 @@ 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
|
||||
|
||||
.PHONY: gen
|
||||
gen: conf proto.h man git-version.h
|
||||
|
||||
.PHONY: gensend
|
||||
gensend: gen
|
||||
if ! diff git-version.h $(srcdir)/gists/rsync-git-version.h >/dev/null; then \
|
||||
./rsync -ai git-version.h $(srcdir)/gists/rsync-git-version.h && \
|
||||
(cd $(srcdir)/gists && git commit --allow-empty-message -m '' rsync-git-version.h && git push) ; \
|
||||
fi
|
||||
rsync -aic $(GENFILES) git-version.h $${SAMBA_HOST-samba.org}:/home/ftp/pub/rsync/generated-files/ || true
|
||||
|
||||
aclocal.m4: $(srcdir)/m4/*.m4
|
||||
aclocal -I $(srcdir)/m4
|
||||
|
||||
@@ -278,6 +280,8 @@ clean: cleantests
|
||||
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) @MAKE_RRSYNC@ \
|
||||
git-version.h rounding rounding.h *.old rsync*.1 rsync*.5 @MAKE_RRSYNC_1@ \
|
||||
*.html daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp
|
||||
rm -f *.gcno *.gcda lib/*.gcno lib/*.gcda zlib/*.gcno zlib/*.gcda popt/*.gcno popt/*.gcda
|
||||
rm -rf coverage coverage-tcp coverage-all coverage-fallback
|
||||
|
||||
.PHONY: cleantests
|
||||
cleantests:
|
||||
@@ -318,30 +322,133 @@ test: check
|
||||
# catch Bash-isms earlier even if we're running on GNU. Of course, we
|
||||
# might lose in the future where POSIX diverges from old sh.
|
||||
|
||||
# `make check` runs tests in parallel by default. Override with
|
||||
# `make check CHECK_J=1` (serial) or any other value.
|
||||
CHECK_J = 8
|
||||
|
||||
# Parallelism for `make coverage`. Defaults to the same as CHECK_J: the
|
||||
# coverage build sets -fprofile-update=atomic (atomic in-memory counters) and
|
||||
# gcc's libgcov serializes the per-source .gcda read-modify-write merge with a
|
||||
# file lock, so concurrent rsync processes (incl. the forked sender/generator/
|
||||
# receiver) accumulate exactly -- verified by a count-linearity check (a hot
|
||||
# line accumulates identically at -j1 and -P16). Override with
|
||||
# `make coverage COVERAGE_J=1` if your libgcov does not lock .gcda merges.
|
||||
COVERAGE_J = $(CHECK_J)
|
||||
|
||||
# Output directory and extra runtests.py flags for `make coverage`. The
|
||||
# `coverage-tcp` target reuses the coverage recipe with --use-tcp (real
|
||||
# loopback rsyncd, which exercises the TCP accept/auth path and the
|
||||
# require_tcp-only tests) and a separate output directory.
|
||||
COVERAGE_DIR = coverage
|
||||
COVERAGE_RUNFLAGS =
|
||||
|
||||
# Bundled third-party code that rsync ships but does not own; excluded from the
|
||||
# coverage report so the percentages reflect rsync's own source. zlib/ and popt/
|
||||
# are wholly vendored; the named lib/ files are PostgreSQL (getaddrinfo) and ISC
|
||||
# (inet_ntop/inet_pton) / standalone (getpass) imports. The other lib/*.c
|
||||
# (md5, mdfour, wildmatch, permstring, pool_alloc, snprintf, sysacls, sysxattrs,
|
||||
# compat) are rsync's own and stay in the report.
|
||||
COVERAGE_EXCLUDE = -e '(^|/)zlib/' -e '(^|/)popt/' \
|
||||
-e '(^|/)lib/(getaddrinfo|getpass|inet_ntop|inet_pton)\.'
|
||||
|
||||
.PHONY: check
|
||||
check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J)
|
||||
|
||||
.PHONY: check29
|
||||
check29: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=29
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J) --protocol=29
|
||||
|
||||
.PHONY: check30
|
||||
check30: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=30
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J) --protocol=30
|
||||
|
||||
# Whole-suite gcov coverage report (HTML, with branch + decision coverage).
|
||||
# Requires a build configured with --enable-coverage and the `gcovr` tool
|
||||
# (pip install gcovr). Runs the suite in parallel (COVERAGE_J, default CHECK_J):
|
||||
# this is safe because the coverage build uses -fprofile-update=atomic and
|
||||
# libgcov locks the per-source .gcda during its merge, so concurrent rsync
|
||||
# processes accumulate exactly (see COVERAGE_J above). Use COVERAGE_J=1 if your
|
||||
# toolchain's libgcov does not lock .gcda merges.
|
||||
.PHONY: coverage
|
||||
coverage: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
@case '$(CFLAGS)' in *--coverage*) ;; \
|
||||
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
|
||||
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
|
||||
find . -name '*.gcda' -delete
|
||||
@rc=0; $(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(COVERAGE_J) $(COVERAGE_RUNFLAGS) || rc=$$?; \
|
||||
rm -rf $(COVERAGE_DIR) && mkdir -p $(COVERAGE_DIR); \
|
||||
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
|
||||
--html-details -o $(COVERAGE_DIR)/index.html . || exit $$?; \
|
||||
echo "Coverage report written to $(COVERAGE_DIR)/index.html"; \
|
||||
if test $$rc != 0; then \
|
||||
echo "*** test suite FAILED (status $$rc) -- coverage report still written above"; \
|
||||
fi; \
|
||||
exit $$rc
|
||||
|
||||
# Same as `make coverage` but with the daemon tests run over a real loopback
|
||||
# rsyncd (--use-tcp), into a separate report directory.
|
||||
.PHONY: coverage-tcp
|
||||
coverage-tcp:
|
||||
$(MAKE) coverage COVERAGE_RUNFLAGS=--use-tcp COVERAGE_DIR=coverage-tcp
|
||||
|
||||
# Comprehensive single report: run the suite under several configurations,
|
||||
# accumulating into the shared .gcda counters (NOT cleared between runs), then
|
||||
# emit one merged, rsync-scoped report. Covers the default (pipe) transport, the
|
||||
# protocol-29/30 compat branches, and the real-TCP daemon path (which also runs
|
||||
# the require_tcp-only tests). Run under sudo to additionally cover root-only
|
||||
# paths (devices, chown, use-chroot, protected-regular). Local target -- CI uses
|
||||
# the plain `coverage`/`coverage-tcp` targets.
|
||||
.PHONY: coverage-all
|
||||
coverage-all: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
@case '$(CFLAGS)' in *--coverage*) ;; \
|
||||
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
|
||||
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
|
||||
find . -name '*.gcda' -delete
|
||||
@rc=0; \
|
||||
for cfg in '' '--protocol=30' '--protocol=29' '--use-tcp'; do \
|
||||
echo "===== coverage-all: runtests.py $$cfg ====="; \
|
||||
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(COVERAGE_J) $$cfg || rc=$$?; \
|
||||
done; \
|
||||
rm -rf coverage-all && mkdir -p coverage-all; \
|
||||
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
|
||||
--html-details -o coverage-all/index.html . || exit $$?; \
|
||||
echo "Merged coverage report written to coverage-all/index.html"; \
|
||||
if test $$rc != 0; then \
|
||||
echo "*** some suite runs FAILED (status $$rc) -- report still written above"; \
|
||||
fi; \
|
||||
exit $$rc
|
||||
|
||||
# Coverage for the portable (non-openat2) resolver tier. Requires a SEPARATE
|
||||
# build configured with --enable-coverage --disable-openat2: its .gcno differ
|
||||
# from the openat2 build, so this report cannot be merged with the others.
|
||||
.PHONY: coverage-fallback
|
||||
coverage-fallback:
|
||||
$(MAKE) coverage COVERAGE_DIR=coverage-fallback
|
||||
|
||||
wildtest.o: wildtest.c t_stub.o lib/wildmatch.c rsync.h config.h
|
||||
wildtest$(EXEEXT): wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@ $(LIBS)
|
||||
|
||||
testsuite/chown-fake.test:
|
||||
ln -s chown.test $(srcdir)/testsuite/chown-fake.test
|
||||
simdtest$(EXEEXT): simd-checksum-x86_64.cpp $(HEADERS)
|
||||
@if test x"@ROLL_SIMD@" != x; then \
|
||||
$(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DTEST_SIMD_CHECKSUM1 \
|
||||
-o $@ $(srcdir)/simd-checksum-x86_64.cpp @ROLL_ASM@ $(LIBS); \
|
||||
else \
|
||||
touch $@; \
|
||||
fi
|
||||
|
||||
testsuite/devices-fake.test:
|
||||
ln -s devices.test $(srcdir)/testsuite/devices-fake.test
|
||||
testsuite/chown-fake_test.py:
|
||||
ln -s chown_test.py $(srcdir)/testsuite/chown-fake_test.py
|
||||
|
||||
testsuite/xattrs-hlink.test:
|
||||
ln -s xattrs.test $(srcdir)/testsuite/xattrs-hlink.test
|
||||
testsuite/devices-fake_test.py:
|
||||
ln -s devices_test.py $(srcdir)/testsuite/devices-fake_test.py
|
||||
|
||||
testsuite/xattrs-hlink_test.py:
|
||||
ln -s xattrs_test.py $(srcdir)/testsuite/xattrs-hlink_test.py
|
||||
|
||||
testsuite/exclude-lsh_test.py:
|
||||
ln -s exclude_test.py $(srcdir)/testsuite/exclude-lsh_test.py
|
||||
|
||||
# This does *not* depend on building or installing: you can use it to
|
||||
# check a version installed from a binary or some other source tree,
|
||||
@@ -349,7 +456,7 @@ testsuite/xattrs-hlink.test:
|
||||
|
||||
.PHONY: installcheck
|
||||
installcheck: $(CHECK_PROGS) $(CHECK_SYMLINKS)
|
||||
POSIXLY_CORRECT=1 TOOLDIR=`pwd` rsync_bin="$(bindir)/rsync$(EXEEXT)" srcdir="$(srcdir)" $(srcdir)/runtests.sh
|
||||
$(srcdir)/runtests.py --rsync-bin="$(bindir)/rsync$(EXEEXT)" --srcdir="$(srcdir)" --tooldir=`pwd` -j $(CHECK_J)
|
||||
|
||||
# TODO: Add 'dist' target; need to know which files will be included
|
||||
|
||||
@@ -366,4 +473,4 @@ doxygen:
|
||||
.PHONY: doxygen-upload
|
||||
doxygen-upload:
|
||||
rsync -avzv $(srcdir)/dox/html/ --delete \
|
||||
$${SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
|
||||
$${RSYNC_SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
|
||||
|
||||
411
NEWS.md
411
NEWS.md
@@ -1,3 +1,410 @@
|
||||
# NEWS for rsync 3.4.3 (20 May 2026)
|
||||
|
||||
## Changes in this version:
|
||||
|
||||
### SECURITY FIXES:
|
||||
|
||||
Six CVEs are fixed in this release. All six are assigned by
|
||||
VulnCheck as CNA. Affected versions are 3.4.2 and earlier in every
|
||||
case. Three of the six (CVE-2026-29518, CVE-2026-43617,
|
||||
CVE-2026-43619) require non-default daemon configuration to reach:
|
||||
the first and third need `use chroot = no` for a module, the second
|
||||
needs `daemon chroot = ...` set in rsyncd.conf. Two (CVE-2026-43618,
|
||||
CVE-2026-43620) are reachable from a normal pull or a normal
|
||||
authenticated daemon connection. The sixth (CVE-2026-45232) is
|
||||
reachable only when `RSYNC_PROXY` is set and the proxy (or a MITM)
|
||||
returns a pathological response. Many thanks to the external
|
||||
researchers who reported these issues.
|
||||
|
||||
- CVE-2026-29518 (CVSS v4.0 7.3, HIGH): TOCTOU symlink race condition
|
||||
allowing local privilege escalation in daemon mode without chroot.
|
||||
An rsync daemon configured with "use chroot = no" was exposed to a
|
||||
time-of-check / time-of-use race on parent path components: a local
|
||||
attacker with write access to a module could replace a parent
|
||||
directory component with a symlink between the receiver's check and
|
||||
its open(), redirecting reads (basis-file disclosure) and writes
|
||||
(file overwrite) outside the module. Default "use chroot = yes" is
|
||||
not exposed. `secure_relative_open()` (added in 3.4.0 for
|
||||
CVE-2024-12086) was previously unused in the daemon-no-chroot
|
||||
case; the fix enables it there and reroutes the sender's
|
||||
read-path opens through it. Reported by Nullx3D (Batuhan Sancak),
|
||||
Damien Neil and Michael Stapelberg.
|
||||
|
||||
- CVE-2026-43617 (CVSS v3.1 4.8, MEDIUM): Hostname/ACL bypass on an
|
||||
rsync daemon configured with `daemon chroot = /X` in rsyncd.conf
|
||||
when the chroot tree lacks DNS resolution support. The
|
||||
reverse-DNS lookup of the connecting client was performed *after*
|
||||
the daemon chroot had been entered; if /X did not contain the
|
||||
libc resolver fixtures (`/etc/resolv.conf`, `/etc/nsswitch.conf`,
|
||||
`/etc/hosts`, NSS service modules) the lookup failed and the
|
||||
connecting hostname was set to "UNKNOWN", causing hostname-based
|
||||
deny rules to silently fail open. IP-based ACLs are unaffected.
|
||||
The per-module `use chroot` setting is unrelated to this issue.
|
||||
The fix performs the lookup before entering the daemon chroot.
|
||||
Reported by MegaManSec.
|
||||
|
||||
- CVE-2026-43618 (CVSS v3.1 8.1, HIGH): Integer overflow in the
|
||||
compressed-token decoder enabling remote memory disclosure to an
|
||||
authenticated daemon peer. The receiver accumulated a 32-bit
|
||||
signed counter without overflow checking; a malicious sender could
|
||||
trigger an overflow that, with careful manipulation, leaked process
|
||||
memory contents to the attacker -- environment variables,
|
||||
passwords, heap and library pointers -- significantly weakening
|
||||
ASLR. The fix bounds the counter and adds wire-input validation in
|
||||
several adjacent places (defence-in-depth). Workaround for older
|
||||
releases: `refuse options = compress` in rsyncd.conf. Reported by
|
||||
Omar Elsayed.
|
||||
|
||||
- CVE-2026-43619 (CVSS v3.1 6.3, MEDIUM): Symlink races on path-based
|
||||
system calls in "use chroot = no" daemon mode (generalisation of
|
||||
CVE-2026-29518). Earlier fixes for symlink races on the receiver's
|
||||
open() call missed the same race class on every other path-based
|
||||
system call: chmod, lchown, utimes, rename, unlink, mkdir, symlink,
|
||||
mknod, link, rmdir and lstat. The fix routes each affected
|
||||
path-based syscall through a parent dirfd opened under
|
||||
RESOLVE_BENEATH-equivalent kernel-enforced confinement (openat2 on
|
||||
Linux 5.6+, O_RESOLVE_BENEATH on FreeBSD 13+ and macOS 15+,
|
||||
per-component O_NOFOLLOW walk elsewhere). Default "use chroot =
|
||||
yes" is not exposed. Reported by Andrew Tridgell as a follow-on
|
||||
audit of CVE-2026-29518.
|
||||
|
||||
- CVE-2026-43620 (CVSS v3.1 6.5, MEDIUM): Out-of-bounds read in the
|
||||
receiver's recv_files() enabling remote denial-of-service of any
|
||||
client pulling from a malicious server (incomplete fix of commit
|
||||
797e17f). The earlier parent_ndx<0 guard added to send_files() was
|
||||
not applied to the visually-identical block in recv_files(). A
|
||||
malicious rsync server can drive any connecting client into a
|
||||
deterministic SIGSEGV by setting CF_INC_RECURSE in the
|
||||
compatibility flags and sending a crafted file list and transfer
|
||||
record. inc_recurse is the protocol-30+ default, so no special
|
||||
options are required on the victim. Workaround for older
|
||||
releases: `--no-inc-recursive` on the client. Reported by Pratham
|
||||
Gupta.
|
||||
|
||||
- CVE-2026-45232 (CVSS v3.1 3.1, LOW): Off-by-one out-of-bounds stack
|
||||
write in the rsync client's HTTP CONNECT proxy handler
|
||||
(`establish_proxy_connection()` in `socket.c`). After issuing the
|
||||
CONNECT request, rsync read the proxy's first response line one
|
||||
byte at a time into a 1024-byte stack buffer with the bound
|
||||
`cp < &buffer[sizeof buffer - 1]`. If the proxy (or a MITM in
|
||||
front of it) returned 1023+ bytes on that first line without a
|
||||
newline terminator, `cp` exited the loop pointing at a buffer slot
|
||||
the loop never wrote, leaving `*cp` holding stale stack data from
|
||||
the earlier `snprintf()` of the outgoing CONNECT request. The
|
||||
post-loop logic then wrote a single `\0` one byte past the end of
|
||||
the buffer on the stack. Reach is client-side only, and only when
|
||||
`RSYNC_PROXY` is set so rsync tunnels an `rsync://` connection
|
||||
through an HTTP CONNECT proxy. The written byte is always `\0`
|
||||
and the offset is fixed by the buffer size, not attacker-chosen,
|
||||
so this is not an arbitrary-write primitive: practical impact is
|
||||
corruption of one adjacent stack byte and possible later
|
||||
misbehaviour or crash. The fix detects the "buffer filled without
|
||||
finding `\n`" case explicitly by position and refuses the response
|
||||
with "proxy response line too long". Reported by Aisle Research
|
||||
via Michal Ruprich (rsync-3.4.1-2.el10 QE).
|
||||
|
||||
In addition to the six CVE fixes, this release adds defence-in-depth
|
||||
hardening on several adjacent paths: bounded wire-supplied counts and
|
||||
lengths in flist/io/acls/xattrs, a guard against length underflow in
|
||||
cumulative `snprintf()` callers, a parent block-index bounds check on
|
||||
the receiver, a NULL check in `read_delay_line()`, a lower ceiling on
|
||||
`MAX_WIRE_DEL_STAT` to avoid signed-int overflow in the
|
||||
`read_del_stats()` accumulator, rejection of hyphen-prefixed
|
||||
remote-shell hostnames (defence-in-depth against argv-injection in
|
||||
tooling that forwards untrusted input into the hostspec position;
|
||||
reported by Aisle Research via Michal Ruprich), and a NULL-check on
|
||||
`localtime_r()` in `timestring()` to keep a malicious server from
|
||||
crashing the client by advertising a file with an out-of-range
|
||||
modtime.
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- Fixed a regression introduced by the 3.4.0 secure_relative_open()
|
||||
CVE fix where legitimate directory symlinks on the receiver side
|
||||
(e.g. when using `-K` / `--copy-dirlinks`) caused "failed
|
||||
verification -- update discarded" errors on delta transfers. The
|
||||
old code rejected every symlink in the path with a per-component
|
||||
`O_NOFOLLOW` walk; the receiver now uses kernel-enforced "stay
|
||||
below dirfd" path resolution where available. Fixes #715.
|
||||
|
||||
### PORTABILITY / BUILD:
|
||||
|
||||
- secure_relative_open() now uses `openat2(RESOLVE_BENEATH |
|
||||
RESOLVE_NO_MAGICLINKS)` on Linux 5.6+, and `openat()` with
|
||||
`O_RESOLVE_BENEATH` on FreeBSD 13+ and macOS 15+ (Sequoia) /
|
||||
iOS 18+. The kernel rejects ".." escapes, absolute symlinks, and
|
||||
symlinks whose target lies outside the starting directory, while
|
||||
still following symlinks that resolve within it -- the same
|
||||
trade-off that fixes the issue #715 regression without weakening
|
||||
the original CVE protection. Other platforms (Solaris, OpenBSD,
|
||||
NetBSD, Cygwin) retain the previous per-component `O_NOFOLLOW`
|
||||
walk; on those platforms the issue #715 regression remains
|
||||
visible.
|
||||
|
||||
- testsuite/xattrs: ignore `SUNWattr_*` in the Solaris `xls`
|
||||
helper.
|
||||
|
||||
### DEVELOPER RELATED:
|
||||
|
||||
- Added testsuite/symlink-dirlink-basis.test (taken from PR #864
|
||||
by Samuel Henrique) covering the issue #715 regression and
|
||||
several edge cases (`--backup`, `--inplace`, `--partial-dir`
|
||||
with protocol < 29, top-level files). The test skips on
|
||||
platforms without a RESOLVE_BENEATH equivalent.
|
||||
|
||||
- Added regression tests for the new security fixes:
|
||||
`chmod-symlink-race.test`, `chdir-symlink-race.test`,
|
||||
`bare-do-open-symlink-race.test`, `alt-dest-symlink-race.test`,
|
||||
`copy-dest-source-symlink.test`, `sender-flist-symlink-leak.test`,
|
||||
`secure-relpath-validation.test`, `daemon-chroot-acl.test` and
|
||||
`daemon-refuse-compress.test`. The symlink-race tests skip on
|
||||
Cygwin, Solaris, OpenBSD and NetBSD (no RESOLVE_BENEATH
|
||||
equivalent on those platforms).
|
||||
|
||||
- runtests.py now errors early with a clear message when any of
|
||||
the test helper programs (`tls`, `trimslash`, `t_unsafe`,
|
||||
`t_chmod_secure`, `t_secure_relpath`, `wildtest`, `getgroups`,
|
||||
`getfsdev`) are missing, instead of letting many tests fail with
|
||||
confusing "not found" errors.
|
||||
|
||||
- Added OpenBSD and NetBSD CI jobs that run `make check` on those
|
||||
platforms.
|
||||
|
||||
- Added Ubuntu 22.04 and AlmaLinux 8 CI workflows so future
|
||||
backports to the two mainstream LTS families build and test on
|
||||
the same CI surface as trunk.
|
||||
|
||||
- testsuite/protected-regular.test now runs unprivileged via
|
||||
`unshare` with user-namespace UID mapping, falling back to skip
|
||||
if `unshare`/`uidmap` is not available; previously it required
|
||||
real root.
|
||||
|
||||
- Added `symlink-dirlink-basis` to the Cygwin CI's expected-skipped
|
||||
list.
|
||||
|
||||
- Removed the old release system (replaced by the new release
|
||||
script in 3.4.2).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# NEWS for rsync 3.4.2 (28 Apr 2026)
|
||||
|
||||
## Changes in this version:
|
||||
|
||||
### SECURITY RELATED:
|
||||
|
||||
Several security-relevant defects were reported and fixed since 3.4.1.
|
||||
None were assigned a CVE — rsync's fork-per-connection design scopes
|
||||
the impact of each of these to the attacker's own connection, which is
|
||||
equivalent to the client closing the socket itself — but they are
|
||||
fixed here as a matter of hygiene and to reduce the chances of a
|
||||
future exploitable combination. Many thanks to the external
|
||||
researchers who reported these issues.
|
||||
|
||||
- Fixed a signed integer overflow in the PROXY protocol v2 header
|
||||
parser: a negative `len` field could bypass the size check and cause
|
||||
a stack buffer overflow in `read_buf()`. Reported by John Walker of
|
||||
ZeroPath.
|
||||
|
||||
- Fixed an invalid access to the files array. Reported by Calum
|
||||
Hutton of Rapid7.
|
||||
|
||||
- Reject negative token values in the compressed-stream token
|
||||
decoder; a negative value could cause callers to misinterpret a
|
||||
missing data pointer as literal data. Reported by Will Sergeant.
|
||||
|
||||
- Fixed the element count passed to the xattr `qsort()` (see
|
||||
https://www.openwall.com/lists/oss-security/2026/04/16/2).
|
||||
|
||||
- Fixed a buffer underflow in `clean_fname()`, and added a regression
|
||||
test.
|
||||
|
||||
- Fixed an uninitialized `mul_one` in the AVX2 get_checksum1 path
|
||||
(undefined behaviour), and added a SIMD-checksum self-test that
|
||||
cross-checks SSE2, SSSE3 and AVX2 against the C reference on both
|
||||
aligned and unaligned buffers.
|
||||
|
||||
- Fixed an uninitialized `buf1` on the first call to
|
||||
`get_checksum2()` in the MD4 path (fixes #673).
|
||||
|
||||
- Zero all new memory from internal allocations: `my_alloc()` now uses
|
||||
`calloc`, and `expand_item_list()` zeros the expanded portion after
|
||||
`realloc`. This gives more predictable behaviour if stale or
|
||||
uninitialised memory is ever accidentally read.
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- Call `tzset()` before chroot so that log timestamps continue to
|
||||
reflect the configured local timezone after the daemon chroots
|
||||
(glibc needs `/etc/localtime`, which is unreachable post-chroot).
|
||||
|
||||
- Use the correct time when writing to the log file.
|
||||
|
||||
- Do not clear `DISPLAY` unconditionally.
|
||||
|
||||
- Fixed a Y2038 bug in `syscall.c` by replacing the `Int32x32To64`
|
||||
macro (which truncates its arguments to 32 bits) with a plain
|
||||
64-bit multiplication.
|
||||
|
||||
- Fixed ACL ID mapping for non-root users (closes #618).
|
||||
|
||||
- Fixed handling of objects with many xattrs on FreeBSD.
|
||||
|
||||
- Fixed `--open-noatime` not taking effect when opening regular
|
||||
files: `O_NOATIME` is now also passed to `do_open_nofollow()`, which
|
||||
has been used for regular files since the CVE fix "fixed symlink
|
||||
race condition in sender".
|
||||
|
||||
- Ignore "directory has vanished" errors.
|
||||
|
||||
- Fixed the removal of multiple leading slashes.
|
||||
|
||||
- Added the missing `--dirs` long option.
|
||||
|
||||
- Fixed a segfault if `poptGetContext()` returns NULL (e.g. under
|
||||
OOM) by not passing NULL to `poptReadDefaultConfig()`. Reported by
|
||||
Ronnie Sahlberg; found with `malloc-fail-tester`.
|
||||
|
||||
- Fixed a build error on ia64 NonStop (which treats missing
|
||||
prototypes as an error, not a warning).
|
||||
|
||||
- Fixed a flaky hardlinks test (fixes #735).
|
||||
|
||||
### ENHANCEMENTS:
|
||||
|
||||
- Added multi-threaded `zstd` compression, gated by a new
|
||||
`--compress-threads=N` option, with validation and man-page
|
||||
coverage.
|
||||
|
||||
- Documented the `temp dir` parameter in the rsyncd.conf man page
|
||||
(fixes #820).
|
||||
|
||||
- Improved rendering of interior dashes in long-option names in
|
||||
`md-convert` (perhaps fixes #686).
|
||||
|
||||
### PORTABILITY / BUILD:
|
||||
|
||||
- Fixed glibc 2.43 const-preserving overloads of `strtok()`,
|
||||
`strchr()` etc. by declaring the affected locals with the right
|
||||
constness. Contributed by Holger Hoffstätte.
|
||||
|
||||
- Converted the bundled zlib 1.2.8 from K&R-style function
|
||||
definitions to ANSI prototypes, so it builds with clang 16+.
|
||||
|
||||
- Avoid using `bool` as an identifier; it is a keyword in C23.
|
||||
|
||||
- `configure.ac`: check for xattr functions in libc first and only
|
||||
fall back to `-lattr`, avoiding spurious overlinking when `-lattr`
|
||||
happens to be installed. Contributed by Eli Schwartz.
|
||||
|
||||
- Made the build reproducible by honouring `SOURCE_DATE_EPOCH` for
|
||||
the manpage date.
|
||||
|
||||
- Removed obsolete `popt/findme.c` and `popt/findme.h` that upstream
|
||||
popt 1.14 folded into `popt.c` (fixes #710). Contributed by Alan
|
||||
Coopersmith.
|
||||
|
||||
### INTERNAL:
|
||||
|
||||
- Made many module-global variables `const` so they can live in
|
||||
`.rodata` and enable additional compiler optimization.
|
||||
|
||||
### DEVELOPER RELATED:
|
||||
|
||||
- Replaced `runtests.sh` with `runtests.py`, a Python test runner
|
||||
that supports `--valgrind` (with per-process log files so valgrind
|
||||
output no longer interferes with output comparisons) and
|
||||
`-j/--parallel` execution for roughly a 7× speed-up on typical
|
||||
hardware.
|
||||
|
||||
- Added a SIMD checksum self-test and a `clean-fname-underflow`
|
||||
regression test.
|
||||
|
||||
- Various CI fixes for macOS and Cygwin (including adding
|
||||
`simd-checksum` to the expected-skipped lists on platforms without
|
||||
SIMD), and tests now run on `ubuntu-latest`.
|
||||
|
||||
- removed support for the unmaintained rsync-patches archive
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# NEWS for rsync 3.4.1 (16 Jan 2025)
|
||||
|
||||
Release 3.4.1 is a fix for regressions introduced in 3.4.0
|
||||
|
||||
## Changes in this version:
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- fixed handling of -H flag with conflict in internal flag values
|
||||
|
||||
- fixed a user after free in logging of failed rename
|
||||
|
||||
- fixed build on systems without openat()
|
||||
|
||||
- removed dependency on alloca() in bundled popt
|
||||
|
||||
### DEVELOPER RELATED:
|
||||
|
||||
- fix to permissions handling in the developer release script
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# NEWS for rsync 3.4.0 (15 Jan 2025)
|
||||
|
||||
Release 3.4.0 is a security release that fixes a number of important vulnerabilities.
|
||||
|
||||
For more details on the vulnerabilities please see the CERT report
|
||||
https://kb.cert.org/vuls/id/952657
|
||||
|
||||
## Changes in this version:
|
||||
|
||||
### PROTOCOL NUMBER:
|
||||
|
||||
- The protocol number was changed to 32 to make it easier for
|
||||
administrators to check their servers have been updated
|
||||
|
||||
### SECURITY FIXES:
|
||||
|
||||
Many thanks to Simon Scannell, Pedro Gallegos, and Jasiel Spelman at
|
||||
Google Cloud Vulnerability Research and Aleksei Gorban (Loqpa) for
|
||||
discovering these vulnerabilities and working with the rsync project
|
||||
to develop and test fixes.
|
||||
|
||||
- CVE-2024-12084 - Heap Buffer Overflow in Checksum Parsing.
|
||||
|
||||
- CVE-2024-12085 - Info Leak via uninitialized Stack contents defeats ASLR.
|
||||
|
||||
- CVE-2024-12086 - Server leaks arbitrary client files.
|
||||
|
||||
- CVE-2024-12087 - Server can make client write files outside of destination directory using symbolic links.
|
||||
|
||||
- CVE-2024-12088 - --safe-links Bypass.
|
||||
|
||||
- CVE-2024-12747 - symlink race condition.
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- Fixed the included popt to avoid a memory error on modern gcc versions.
|
||||
|
||||
- Fixed an incorrect extern variable's type that caused an ACL issue on macOS.
|
||||
|
||||
- Fixed IPv6 configure check
|
||||
|
||||
### INTERNAL:
|
||||
|
||||
- Updated included popt to version 1.19.
|
||||
|
||||
### DEVELOPER RELATED:
|
||||
|
||||
- Various improvements to the release scripts and git setup.
|
||||
|
||||
- Improved packaging/var-checker to identify variable type issues.
|
||||
|
||||
- added FreeBSD and Solaris CI builds
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
# NEWS for rsync 3.3.0 (6 Apr 2024)
|
||||
|
||||
## Changes in this version:
|
||||
@@ -4762,6 +5169,10 @@
|
||||
|
||||
| RELEASE DATE | VER. | DATE OF COMMIT\* | PROTOCOL |
|
||||
|--------------|--------|------------------|-------------|
|
||||
| 20 May 2026 | 3.4.3 | | 32 |
|
||||
| 28 Apr 2026 | 3.4.2 | | 32 |
|
||||
| 16 Jan 2025 | 3.4.1 | | 32 |
|
||||
| 15 Jan 2025 | 3.4.0 | 15 Jan 2025 | 32 |
|
||||
| 06 Apr 2024 | 3.3.0 | | 31 |
|
||||
| 20 Oct 2022 | 3.2.7 | | 31 |
|
||||
| 09 Sep 2022 | 3.2.6 | | 31 |
|
||||
|
||||
21
README.md
21
README.md
@@ -34,7 +34,7 @@ If you need to build rsync yourself, check out the [INSTALL][1] page for
|
||||
information on what libraries and packages you can use to get the maximum
|
||||
features in your build.
|
||||
|
||||
[1]: https://github.com/WayneD/rsync/blob/master/INSTALL.md
|
||||
[1]: https://github.com/RsyncProject/rsync/blob/master/INSTALL.md
|
||||
|
||||
SETUP
|
||||
-----
|
||||
@@ -93,6 +93,15 @@ details.
|
||||
[3]: https://rsync.samba.org/lists.html
|
||||
|
||||
|
||||
DISCORD
|
||||
-------
|
||||
|
||||
There is also an rsync [Discord server][d] for real-time chat about rsync
|
||||
and its development.
|
||||
|
||||
[d]: https://discord.gg/Avfvy9zhdp
|
||||
|
||||
|
||||
BUG REPORTS
|
||||
-----------
|
||||
|
||||
@@ -112,6 +121,7 @@ page of the web site.
|
||||
|
||||
Alternately, email your bug report to <rsync@lists.samba.org>.
|
||||
|
||||
For security issues please email details of the issue to <rsync.project@gmail.com>.
|
||||
|
||||
GIT REPOSITORY
|
||||
--------------
|
||||
@@ -120,7 +130,7 @@ If you want to get the very latest version of rsync direct from the
|
||||
source code repository, then you will need to use git. The git repo
|
||||
is hosted [on GitHub][6] and [on Samba's site][7].
|
||||
|
||||
[6]: https://github.com/WayneD/rsync
|
||||
[6]: https://github.com/RsyncProject/rsync
|
||||
[7]: https://git.samba.org/?p=rsync.git;a=summary
|
||||
|
||||
See [the download page][8] for full details on all the ways to grab the
|
||||
@@ -132,13 +142,12 @@ source.
|
||||
COPYRIGHT
|
||||
---------
|
||||
|
||||
Rsync was originally written by Andrew Tridgell and is currently
|
||||
maintained by Wayne Davison. It has been improved by many developers
|
||||
from around the world.
|
||||
Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
|
||||
people from around the world have helped to maintain and improve it.
|
||||
|
||||
Rsync may be used, modified and redistributed only under the terms of
|
||||
the GNU General Public License, found in the file [COPYING][9] in this
|
||||
distribution, or at [the Free Software Foundation][10].
|
||||
|
||||
[9]: https://github.com/WayneD/rsync/blob/master/COPYING
|
||||
[9]: https://github.com/RsyncProject/rsync/blob/master/COPYING
|
||||
[10]: https://www.fsf.org/licenses/gpl.html
|
||||
|
||||
@@ -9,4 +9,5 @@ help backporting fixes into an older release, feel free to ask.
|
||||
|
||||
Email your vulnerability information to rsync's maintainer:
|
||||
|
||||
Wayne Davison <wayne@opencoder.net>
|
||||
Rsync Project <rsync.project@gmail.com>
|
||||
|
||||
|
||||
2
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, const char *tok)
|
||||
static int match_address(const char *addr, char *tok)
|
||||
{
|
||||
char *p;
|
||||
struct addrinfo hints, *resa, *rest;
|
||||
|
||||
9
acls.c
9
acls.c
@@ -28,7 +28,7 @@ extern int dry_run;
|
||||
extern int am_root;
|
||||
extern int read_only;
|
||||
extern int list_only;
|
||||
extern int orig_umask;
|
||||
extern mode_t orig_umask;
|
||||
extern int numeric_ids;
|
||||
extern int inc_recurse;
|
||||
extern int preserve_devices;
|
||||
@@ -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(f);
|
||||
int i, count = read_varint_bounded(f, 0, MAX_WIRE_ACL_COUNT, "ACL count");
|
||||
|
||||
ent->idas = count ? new_array(id_access, count) : NULL;
|
||||
ent->count = count;
|
||||
@@ -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 && am_root && !numeric_ids)
|
||||
if (inc_recurse && !numeric_ids)
|
||||
id = match_uid(id);
|
||||
} else {
|
||||
if (inc_recurse && (!am_root || !numeric_ids))
|
||||
@@ -765,6 +765,7 @@ static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode
|
||||
/* If we received a superfluous mask, throw it away. */
|
||||
duo_item->racl.mask_obj = NO_ENTRY;
|
||||
(void)mode;
|
||||
(void)computed_mask_bits;
|
||||
#else
|
||||
if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
|
||||
/* Mask must be non-empty with lists. */
|
||||
@@ -981,7 +982,7 @@ static int set_rsync_acl(const char *fname, acl_duo *duo_item,
|
||||
&& !pack_smb_acl(&duo_item->sacl, &duo_item->racl))
|
||||
return -1;
|
||||
#ifdef HAVE_OSX_ACLS
|
||||
mode = 0; /* eliminate compiler warning */
|
||||
(void)mode; /* eliminate compiler warning */
|
||||
#else
|
||||
if (type == SMB_ACL_TYPE_ACCESS) {
|
||||
cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl, cur_mode, mode);
|
||||
|
||||
14
backup.c
14
backup.c
@@ -39,7 +39,7 @@ static int validate_backup_dir(void)
|
||||
{
|
||||
STRUCT_STAT st;
|
||||
|
||||
if (do_lstat(backup_dir_buf, &st) < 0) {
|
||||
if (do_lstat_at(backup_dir_buf, &st) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
|
||||
@@ -98,7 +98,7 @@ static BOOL copy_valid_path(const char *fname)
|
||||
for ( ; b; name = b + 1, b = strchr(name, '/')) {
|
||||
*b = '\0';
|
||||
|
||||
while (do_mkdir(backup_dir_buf, ACCESSPERMS) < 0) {
|
||||
while (do_mkdir_at(backup_dir_buf, ACCESSPERMS) < 0) {
|
||||
if (errno == EEXIST) {
|
||||
val = validate_backup_dir();
|
||||
if (val > 0)
|
||||
@@ -197,7 +197,7 @@ static inline int link_or_rename(const char *from, const char *to,
|
||||
if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
|
||||
return 0; /* Use copy code. */
|
||||
#endif
|
||||
if (do_link(from, to) == 0) {
|
||||
if (do_link_at(from, to) == 0) {
|
||||
if (DEBUG_GTE(BACKUP, 1))
|
||||
rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
|
||||
return 2;
|
||||
@@ -207,7 +207,7 @@ static inline int link_or_rename(const char *from, const char *to,
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (do_rename(from, to) == 0) {
|
||||
if (do_rename_at(from, to) == 0) {
|
||||
if (stp->st_nlink > 1 && !S_ISDIR(stp->st_mode)) {
|
||||
/* If someone has hard-linked the file into the backup
|
||||
* dir, rename() might return success but do nothing! */
|
||||
@@ -246,7 +246,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
|
||||
goto success;
|
||||
if (errno == EEXIST || errno == EISDIR) {
|
||||
STRUCT_STAT bakst;
|
||||
if (do_lstat(buf, &bakst) == 0) {
|
||||
if (do_lstat_at(buf, &bakst) == 0) {
|
||||
int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
|
||||
if (delete_item(buf, bakst.st_mode, flags) != 0)
|
||||
return 0;
|
||||
@@ -277,7 +277,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
|
||||
/* Check to see if this is a device file, or link */
|
||||
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|
||||
|| (preserve_specials && IS_SPECIAL(file->mode))) {
|
||||
if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
|
||||
if (do_mknod_at(buf, file->mode, sx.st.st_rdev) < 0)
|
||||
rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf));
|
||||
else if (DEBUG_GTE(BACKUP, 1))
|
||||
rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname);
|
||||
@@ -294,7 +294,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
|
||||
}
|
||||
ret = 2;
|
||||
} else {
|
||||
if (do_symlink(sl, buf) < 0)
|
||||
if (do_symlink_at(sl, buf) < 0)
|
||||
rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl);
|
||||
else if (DEBUG_GTE(BACKUP, 1))
|
||||
rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname);
|
||||
|
||||
2
batch.c
2
batch.c
@@ -75,7 +75,7 @@ static int *flag_ptr[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *flag_name[] = {
|
||||
static const char *const flag_name[] = {
|
||||
"--recurse (-r)",
|
||||
"--owner (-o)",
|
||||
"--group (-g)",
|
||||
|
||||
@@ -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 {
|
||||
char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
|
||||
const 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);
|
||||
@@ -366,9 +366,8 @@ void get_checksum2(char *buf, int32 len, char *sum)
|
||||
|
||||
mdfour_begin(&m);
|
||||
|
||||
if (len > len1) {
|
||||
if (buf1)
|
||||
free(buf1);
|
||||
if (len > len1 || !buf1) {
|
||||
free(buf1);
|
||||
buf1 = new_array(char, len+4);
|
||||
len1 = len;
|
||||
}
|
||||
@@ -406,7 +405,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
|
||||
int32 remainder;
|
||||
int fd;
|
||||
|
||||
fd = do_open(fname, O_RDONLY, 0);
|
||||
fd = do_open_checklinks(fname);
|
||||
if (fd == -1) {
|
||||
memset(sum, 0, file_sum_len);
|
||||
return;
|
||||
|
||||
12
cleanup.c
12
cleanup.c
@@ -198,7 +198,7 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
|
||||
switch_step++;
|
||||
|
||||
if (cleanup_fname)
|
||||
do_unlink(cleanup_fname);
|
||||
do_unlink_at(cleanup_fname);
|
||||
if (exit_code)
|
||||
kill_all(SIGUSR1);
|
||||
if (cleanup_pid && cleanup_pid == getpid()) {
|
||||
@@ -269,8 +269,16 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
|
||||
break;
|
||||
}
|
||||
|
||||
if (called_from_signal_handler)
|
||||
if (called_from_signal_handler) {
|
||||
#ifdef GCOV_COVERAGE
|
||||
/* _exit() bypasses the gcov atexit flush; rsync's generator (and
|
||||
* other processes) normally finish via the signal handler, so
|
||||
* without this they would write no .gcda. Harmless otherwise. */
|
||||
extern void __gcov_dump(void);
|
||||
__gcov_dump();
|
||||
#endif
|
||||
_exit(exit_code);
|
||||
}
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ int read_proxy_protocol_header(int fd)
|
||||
char sig[PROXY_V2_SIG_SIZE];
|
||||
char ver_cmd;
|
||||
char fam;
|
||||
char len[2];
|
||||
unsigned char len[2];
|
||||
union {
|
||||
struct {
|
||||
char src_addr[4];
|
||||
|
||||
@@ -30,6 +30,7 @@ extern int list_only;
|
||||
extern int am_sender;
|
||||
extern int am_server;
|
||||
extern int am_daemon;
|
||||
extern int am_chrooted;
|
||||
extern int am_root;
|
||||
extern int msgs2stderr;
|
||||
extern int rsync_port;
|
||||
@@ -38,6 +39,7 @@ extern int ignore_errors;
|
||||
extern int preserve_xattrs;
|
||||
extern int kluge_around_eof;
|
||||
extern int munge_symlinks;
|
||||
extern int use_secure_symlinks;
|
||||
extern int open_noatime;
|
||||
extern int sanitize_paths;
|
||||
extern int numeric_ids;
|
||||
@@ -976,11 +978,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1003,6 +1008,15 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable secure symlink handling for any non-chrooted daemon module.
|
||||
* This prevents TOCTOU race attacks where an attacker could switch a
|
||||
* directory to a symlink between path validation and file open.
|
||||
* Match the gate used by the do_*_at() wrappers in syscall.c
|
||||
* (am_daemon && !am_chrooted) -- the protection has nothing to do
|
||||
* with symlink munging, so a module configured with
|
||||
* "munge symlinks = false" must still get the secure-open path. */
|
||||
use_secure_symlinks = am_daemon && !am_chrooted;
|
||||
|
||||
if (gid_list.count) {
|
||||
gid_t *gid_array = gid_list.items;
|
||||
if (setgid(gid_array[0])) {
|
||||
@@ -1298,13 +1312,49 @@ 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;
|
||||
|
||||
5
compat.c
5
compat.c
@@ -52,6 +52,7 @@ extern int need_messages_from_generator;
|
||||
extern int delete_mode, delete_before, delete_during, delete_after;
|
||||
extern int do_compression;
|
||||
extern int do_compression_level;
|
||||
extern int do_compression_threads;
|
||||
extern int saw_stderr_opt;
|
||||
extern int msgs2stderr;
|
||||
extern char *shell_cmd;
|
||||
@@ -131,7 +132,7 @@ static const char *client_info;
|
||||
* of that protocol for it to be advertised as available. */
|
||||
static void check_sub_protocol(void)
|
||||
{
|
||||
char *dot;
|
||||
const char *dot;
|
||||
int their_protocol, their_sub;
|
||||
int our_sub = get_subprotocol_version();
|
||||
|
||||
@@ -414,7 +415,7 @@ static const char *getenv_nstr(int ntype)
|
||||
env_str = ntype == NSTR_COMPRESS ? "zlib" : protocol_version >= 30 ? "md5" : "md4";
|
||||
|
||||
if (am_server && env_str) {
|
||||
char *cp = strchr(env_str, '&');
|
||||
const char *cp = strchr(env_str, '&');
|
||||
if (cp)
|
||||
env_str = cp + 1;
|
||||
}
|
||||
|
||||
76
configure.ac
76
configure.ac
@@ -82,6 +82,32 @@ if test x"$enable_profile" = x"yes"; then
|
||||
CFLAGS="$CFLAGS -pg"
|
||||
fi
|
||||
|
||||
dnl Coverage build (gcov) for `make coverage`. NOTE: --enable-profile above is
|
||||
dnl gprof (-pg) and is NOT coverage. -O0 keeps branch coverage meaningful;
|
||||
dnl -fprofile-update=atomic keeps the shared .gcda counters correct while the
|
||||
dnl suite runs many rsync processes in parallel.
|
||||
AC_ARG_ENABLE(coverage,
|
||||
AS_HELP_STRING([--enable-coverage],[build with gcov instrumentation for `make coverage`]))
|
||||
if test x"$enable_coverage" = x"yes"; then
|
||||
CFLAGS="$CFLAGS --coverage -fprofile-update=atomic -O0"
|
||||
CXXFLAGS="$CXXFLAGS --coverage -fprofile-update=atomic -O0"
|
||||
LDFLAGS="$LDFLAGS --coverage"
|
||||
AC_DEFINE([GCOV_COVERAGE], 1,
|
||||
[Flush gcov counters at exit_cleanup: rsync's children exit via _exit(), which bypasses the gcov atexit handler, so without this no .gcda is written for the receiver/generator/daemon-worker processes.])
|
||||
fi
|
||||
|
||||
dnl openat2(RESOLVE_BENEATH) is used on Linux 5.6+ for the secure resolver.
|
||||
dnl --disable-openat2 forces the portable per-component O_NOFOLLOW fallback to
|
||||
dnl run as the primary resolver on ordinary Linux, so that tier is exercised
|
||||
dnl (and coverage-counted) without needing a pre-5.6 kernel. Behaviour-neutral
|
||||
dnl by default (the knob only REMOVES a tier when explicitly disabled).
|
||||
AC_ARG_ENABLE(openat2,
|
||||
AS_HELP_STRING([--disable-openat2],[do not use Linux openat2(RESOLVE_BENEATH); force the portable resolver (for exercising the fallback tier)]))
|
||||
if test x"$enable_openat2" != x"no"; then
|
||||
AC_DEFINE([HAVE_OPENAT2], 1,
|
||||
[Define to use Linux openat2(RESOLVE_BENEATH) in secure_relative_open where available.])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if md2man can create manpages])
|
||||
if test x"$ac_cv_path_PYTHON3" = x; then
|
||||
AC_MSG_RESULT(no - python3 not found)
|
||||
@@ -392,7 +418,7 @@ AS_HELP_STRING([--disable-ipv6],[disable to omit ipv6 support]),
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
main()
|
||||
int main()
|
||||
{
|
||||
if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
|
||||
exit(1);
|
||||
@@ -424,6 +450,26 @@ case $host_os in
|
||||
* ) AC_MSG_RESULT(no);;
|
||||
esac
|
||||
|
||||
# We default to using our zlib unless --with-included-zlib=no is given.
|
||||
if test x"$with_included_zlib" != x"no"; then
|
||||
with_included_zlib=yes
|
||||
elif test x"$ac_cv_header_zlib_h" != x"yes"; then
|
||||
with_included_zlib=yes
|
||||
fi
|
||||
if test x"$with_included_zlib" != x"yes"; then
|
||||
AC_CHECK_LIB(z, deflateParams, , [with_included_zlib=yes])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to use included zlib])
|
||||
if test x"$with_included_zlib" = x"yes"; then
|
||||
AC_MSG_RESULT($srcdir/zlib)
|
||||
BUILD_ZLIB='$(zlib_OBJS)'
|
||||
CFLAGS="-I$srcdir/zlib $CFLAGS"
|
||||
else
|
||||
AC_DEFINE(EXTERNAL_ZLIB, 1, [Define to 1 if using external zlib])
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to enable use of openssl crypto library])
|
||||
AC_ARG_ENABLE([openssl],
|
||||
AS_HELP_STRING([--disable-openssl],[disable to omit openssl crypto library]))
|
||||
@@ -573,7 +619,7 @@ if test x"$no_lib" != x; then
|
||||
echo ""
|
||||
echo "See the INSTALL file for hints on how to install the missing libraries and/or"
|
||||
echo "how to generate (or fetch) manpages:"
|
||||
echo " https://github.com/WayneD/rsync/blob/master/INSTALL.md"
|
||||
echo " https://github.com/RsyncProject/rsync/blob/master/INSTALL.md"
|
||||
echo ""
|
||||
echo "To disable one or more features, the relevant configure options are:"
|
||||
for lib in $no_lib; do
|
||||
@@ -870,7 +916,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd chown chmod lchmod mknod mkfifo \
|
||||
fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
|
||||
chflags getattrlist mktime innetgr linkat \
|
||||
memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
|
||||
strlcat strlcpy strtol mallinfo mallinfo2 getgroups setgroups geteuid getegid \
|
||||
strlcat strlcpy stpcpy strtol mallinfo mallinfo2 getgroups setgroups geteuid getegid \
|
||||
setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
|
||||
seteuid strerror putenv iconv_open locale_charset nl_langinfo getxattr \
|
||||
extattr_get_link sigaction sigprocmask setattrlist getgrouplist \
|
||||
@@ -1084,6 +1130,8 @@ if test x"$with_included_popt" = x"yes"; then
|
||||
AC_MSG_RESULT($srcdir/popt)
|
||||
BUILD_POPT='$(popt_OBJS)'
|
||||
CFLAGS="-I$srcdir/popt $CFLAGS"
|
||||
AC_DEFINE(POPT_SYSCONFDIR, "/etc", [sysconfig dir for popt])
|
||||
AC_DEFINE(PACKAGE, "rsync", [package name for rsync])
|
||||
if test x"$ALLOCA" != x
|
||||
then
|
||||
# this can be removed when/if we add an included alloca.c;
|
||||
@@ -1094,26 +1142,6 @@ else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
# We default to using our zlib unless --with-included-zlib=no is given.
|
||||
if test x"$with_included_zlib" != x"no"; then
|
||||
with_included_zlib=yes
|
||||
elif test x"$ac_cv_header_zlib_h" != x"yes"; then
|
||||
with_included_zlib=yes
|
||||
fi
|
||||
if test x"$with_included_zlib" != x"yes"; then
|
||||
AC_CHECK_LIB(z, deflateParams, , [with_included_zlib=yes])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether to use included zlib])
|
||||
if test x"$with_included_zlib" = x"yes"; then
|
||||
AC_MSG_RESULT($srcdir/zlib)
|
||||
BUILD_ZLIB='$(zlib_OBJS)'
|
||||
CFLAGS="-I$srcdir/zlib $CFLAGS"
|
||||
else
|
||||
AC_DEFINE(EXTERNAL_ZLIB, 1, [Define to 1 if using external zlib])
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for unsigned char],rsync_cv_SIGNED_CHAR_OK,[
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[signed char *s = (signed char *)""]])],[rsync_cv_SIGNED_CHAR_OK=yes],[rsync_cv_SIGNED_CHAR_OK=no])])
|
||||
if test x"$rsync_cv_SIGNED_CHAR_OK" = x"yes"; then
|
||||
@@ -1390,7 +1418,7 @@ else
|
||||
AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs (or equivalent)])
|
||||
AC_DEFINE(SUPPORT_XATTRS, 1)
|
||||
AC_DEFINE(NO_SYMLINK_USER_XATTRS, 1, [True if symlinks do not support user xattrs])
|
||||
AC_CHECK_LIB(attr,getxattr)
|
||||
AC_SEARCH_LIBS(getxattr,attr)
|
||||
;;
|
||||
darwin*)
|
||||
AC_MSG_RESULT(Using OS X xattrs)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
BEGIN {
|
||||
heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from a list of values in " ARGV[1] "! */\n\n"
|
||||
sect = psect = defines = accessors = prior_ptype = ""
|
||||
parms = "\nstatic struct parm_struct parm_table[] = {"
|
||||
parms = "\nstatic const struct parm_struct parm_table[] = {"
|
||||
comment_fmt = "\n/********** %s **********/\n"
|
||||
tdstruct = "typedef struct {"
|
||||
}
|
||||
|
||||
6
delete.c
6
delete.c
@@ -98,7 +98,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
|
||||
|
||||
strlcpy(p, fp->basename, remainder);
|
||||
if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
|
||||
do_chmod(fname, fp->mode | S_IWUSR);
|
||||
do_chmod_at(fname, fp->mode | S_IWUSR);
|
||||
/* Save stack by recursing to ourself directly. */
|
||||
if (S_ISDIR(fp->mode)) {
|
||||
if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
|
||||
@@ -139,7 +139,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
|
||||
}
|
||||
|
||||
if (flags & DEL_NO_UID_WRITE)
|
||||
do_chmod(fbuf, mode | S_IWUSR);
|
||||
do_chmod_at(fbuf, mode | S_IWUSR);
|
||||
|
||||
if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
|
||||
/* This only happens on the first call to delete_item() since
|
||||
@@ -160,7 +160,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
what = "rmdir";
|
||||
ok = do_rmdir(fbuf) == 0;
|
||||
ok = do_rmdir_at(fbuf) == 0;
|
||||
} else {
|
||||
if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
|
||||
what = "make_backup";
|
||||
|
||||
@@ -904,7 +904,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;
|
||||
char *p, *pattern = ex->pattern;
|
||||
const char *p, *pattern = ex->pattern;
|
||||
const char *strings[16]; /* more than enough */
|
||||
const char *name = fname + (*fname == '/');
|
||||
|
||||
|
||||
36
flist.c
36
flist.c
@@ -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(f);
|
||||
(void)read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
#else
|
||||
modtime_nsec = read_varint(f);
|
||||
modtime_nsec = read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
|
||||
else
|
||||
modtime_nsec = 0;
|
||||
#endif
|
||||
@@ -861,8 +861,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (!(xflags & XMIT_SAME_MODE))
|
||||
if (!(xflags & XMIT_SAME_MODE)) {
|
||||
mode = from_wire_mode(read_int(f));
|
||||
/* Reject modes whose type bits are not one of the standard
|
||||
* file types; otherwise garbage mode values propagate through
|
||||
* the file-type checks below unpredictably. */
|
||||
if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)
|
||||
&& !S_ISCHR(mode) && !S_ISBLK(mode)
|
||||
&& !S_ISFIFO(mode) && !S_ISSOCK(mode)) {
|
||||
rprintf(FERROR, "invalid file mode 0%o for %s [%s]\n",
|
||||
(unsigned)mode, lastname, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
}
|
||||
if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
|
||||
atime = read_varlong(f, 4);
|
||||
#if SIZEOF_TIME_T < SIZEOF_INT64
|
||||
@@ -1390,7 +1401,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(fname, O_RDONLY, 0);
|
||||
int fd = do_open_checklinks(fname);
|
||||
if (fd >= 0) {
|
||||
st.st_size = get_device_size(fd, fname);
|
||||
close(fd);
|
||||
@@ -2584,6 +2595,19 @@ struct file_list *recv_file_list(int f, int dir_ndx)
|
||||
init_hard_links();
|
||||
#endif
|
||||
|
||||
if (inc_recurse && dir_ndx >= 0) {
|
||||
if (dir_ndx >= dir_flist->used) {
|
||||
rprintf(FERROR_XFER, "rsync: refusing invalid dir_ndx %u >= %u\n", dir_ndx, dir_flist->used);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
struct file_struct *file = dir_flist->files[dir_ndx];
|
||||
if (file->flags & FLAG_GOT_DIR_FLIST) {
|
||||
rprintf(FERROR_XFER, "rsync: refusing malicious duplicate flist for dir %d\n", dir_ndx);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
file->flags |= FLAG_GOT_DIR_FLIST;
|
||||
}
|
||||
|
||||
flist = flist_new(0, "recv_file_list");
|
||||
flist_expand(flist, FLIST_START_LARGE);
|
||||
|
||||
@@ -3154,8 +3178,8 @@ static void output_flist(struct file_list *flist)
|
||||
} else
|
||||
*uidbuf = '\0';
|
||||
if (gid_ndx) {
|
||||
static char parens[] = "(\0)\0\0\0";
|
||||
char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
|
||||
static const char parens[] = "(\0)\0\0\0";
|
||||
const char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
|
||||
snprintf(gidbuf, sizeof gidbuf, " gid=%s%u%s",
|
||||
pp, F_GROUP(file), pp + 2);
|
||||
} else
|
||||
|
||||
56
generator.c
56
generator.c
@@ -229,11 +229,13 @@ static int read_delay_line(char *buf, int *flags_p)
|
||||
*flags_p = 0;
|
||||
|
||||
if (sscanf(bp, "%x ", &mode) != 1) {
|
||||
invalid_data:
|
||||
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
|
||||
return -1;
|
||||
goto invalid_data;
|
||||
}
|
||||
past_space = strchr(bp, ' ') + 1;
|
||||
past_space = strchr(bp, ' ');
|
||||
if (!past_space) {
|
||||
goto invalid_data;
|
||||
}
|
||||
past_space++;
|
||||
len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
|
||||
read_pos = j + 1;
|
||||
|
||||
@@ -247,6 +249,10 @@ static int read_delay_line(char *buf, int *flags_p)
|
||||
memcpy(buf, past_space, len);
|
||||
|
||||
return mode;
|
||||
|
||||
invalid_data:
|
||||
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void do_delayed_deletions(char *delbuf)
|
||||
@@ -984,7 +990,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
if (find_exact_for_existing) {
|
||||
if (alt_dest_type == LINK_DEST && real_st.st_dev == sxp->st.st_dev && real_st.st_ino == sxp->st.st_ino)
|
||||
return -1;
|
||||
if (do_unlink(fname) < 0 && errno != ENOENT)
|
||||
if (do_unlink_at(fname) < 0 && errno != ENOENT)
|
||||
goto got_nothing_for_ya;
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
@@ -1112,7 +1118,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
&& !IS_SPECIAL(file->mode) && !IS_DEVICE(file->mode)
|
||||
#endif
|
||||
&& !S_ISDIR(file->mode)) {
|
||||
if (do_link(cmpbuf, fname) < 0) {
|
||||
if (do_link_at(cmpbuf, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to hard-link %s with %s",
|
||||
cmpbuf, fname);
|
||||
@@ -1315,7 +1321,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
}
|
||||
if (relative_paths && !implied_dirs && file->mode != 0
|
||||
&& do_stat(dn, &sx.st) < 0) {
|
||||
&& do_stat_at(dn, &sx.st) < 0) {
|
||||
if (dry_run)
|
||||
goto parent_is_dry_missing;
|
||||
if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) {
|
||||
@@ -1427,7 +1433,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
&& (stype == FT_DIR
|
||||
|| delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0))
|
||||
goto cleanup; /* Any errors get reported later. */
|
||||
if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0)
|
||||
if (do_mkdir_at(fname, (file->mode|added_perms) & 0700) == 0)
|
||||
file->flags |= FLAG_DIR_CREATED;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1469,10 +1475,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
itemize(fnamecmp, file, ndx, statret, &sx,
|
||||
statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
|
||||
}
|
||||
if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
|
||||
if (real_ret != 0 && do_mkdir_at(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
|
||||
if (!relative_paths || errno != ENOENT
|
||||
|| make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0
|
||||
|| (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
|| (do_mkdir_at(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"recv_generator: mkdir %s failed",
|
||||
full_fname(fname));
|
||||
@@ -1499,7 +1505,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
#ifdef HAVE_CHMOD
|
||||
if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
|
||||
mode_t mode = file->mode | S_IRWXU;
|
||||
if (do_chmod(fname, mode) < 0) {
|
||||
if (do_chmod_at(fname, mode) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"failed to modify permissions on %s",
|
||||
full_fname(fname));
|
||||
@@ -1798,7 +1804,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(fnamecmp, O_RDONLY, 0)) >= 0)
|
||||
if ((fd = do_open_nofollow(fnamecmp, O_RDONLY)) >= 0)
|
||||
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
|
||||
}
|
||||
|
||||
@@ -1808,7 +1814,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(partialptr);
|
||||
do_unlink_at(partialptr);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT | maybe_ATTRS_ACCURATE_TIME);
|
||||
@@ -1867,7 +1873,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
if (fd < 0 && (fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
|
||||
if (fd < 0 && (fd = do_open_checklinks(fnamecmp)) < 0) {
|
||||
rsyserr(FERROR, errno, "failed to open %s, continuing",
|
||||
full_fname(fnamecmp));
|
||||
pretend_missing:
|
||||
@@ -1896,7 +1902,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
back_file = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
if ((f_copy = do_open_at(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "open %s", full_fname(backupptr));
|
||||
unmake_file(back_file);
|
||||
back_file = NULL;
|
||||
@@ -2016,7 +2022,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
|
||||
if (slnk) {
|
||||
#ifdef SUPPORT_LINKS
|
||||
if (do_symlink(slnk, create_name) < 0) {
|
||||
if (do_symlink_at(slnk, create_name) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
|
||||
full_fname(create_name), slnk);
|
||||
return 0;
|
||||
@@ -2032,7 +2038,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
return 0;
|
||||
#endif
|
||||
} else {
|
||||
if (do_mknod(create_name, file->mode, rdev) < 0) {
|
||||
if (do_mknod_at(create_name, file->mode, rdev) < 0) {
|
||||
rsyserr(FERROR_XFER, errno, "mknod %s failed",
|
||||
full_fname(create_name));
|
||||
return 0;
|
||||
@@ -2040,10 +2046,14 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
|
||||
}
|
||||
|
||||
if (!skip_atomic) {
|
||||
if (do_rename(tmpname, fname) < 0) {
|
||||
if (do_rename_at(tmpname, fname) < 0) {
|
||||
char *full_tmpname = strdup(full_fname(tmpname));
|
||||
if (full_tmpname == NULL)
|
||||
out_of_memory("atomic_create");
|
||||
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
|
||||
full_fname(tmpname), full_fname(fname));
|
||||
do_unlink(tmpname);
|
||||
full_tmpname, full_fname(fname));
|
||||
free(full_tmpname);
|
||||
do_unlink_at(tmpname);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2107,7 +2117,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
|
||||
continue;
|
||||
fname = f_name(file, NULL);
|
||||
if (fix_dir_perms)
|
||||
do_chmod(fname, file->mode);
|
||||
do_chmod_at(fname, file->mode);
|
||||
if (need_retouch_dir_times) {
|
||||
STRUCT_STAT st;
|
||||
if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
|
||||
@@ -2142,6 +2152,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
|
||||
if (send_failed)
|
||||
ndx = get_hlink_num();
|
||||
flist = flist_for_ndx(ndx, "check_for_finished_files.1");
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
file = flist->files[ndx - flist->ndx_start];
|
||||
assert(file->flags & FLAG_HLINKED);
|
||||
if (send_failed)
|
||||
@@ -2170,6 +2182,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
|
||||
|
||||
flist = cur_flist;
|
||||
cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2");
|
||||
if (ndx < cur_flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
|
||||
file = cur_flist->files[ndx - cur_flist->ndx_start];
|
||||
if (solo_file)
|
||||
|
||||
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 (*)()) hlink_compare_gnum);
|
||||
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)(const void*, const void*))hlink_compare_gnum);
|
||||
|
||||
for (from = 0; from < ndx_count; from++) {
|
||||
file = hlink_flist->sorted[ndx_list[from]];
|
||||
@@ -454,7 +454,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
|
||||
int hard_link_one(struct file_struct *file, const char *fname,
|
||||
const char *oldname, int terse)
|
||||
{
|
||||
if (do_link(oldname, fname) < 0) {
|
||||
if (do_link_at(oldname, fname) < 0) {
|
||||
enum logcode code;
|
||||
if (terse) {
|
||||
if (!INFO_GTE(NAME, 1))
|
||||
|
||||
66
io.c
66
io.c
@@ -55,6 +55,7 @@ extern int read_batch;
|
||||
extern int compat_flags;
|
||||
extern int protect_args;
|
||||
extern int checksum_seed;
|
||||
extern int xfer_sum_len;
|
||||
extern int daemon_connection;
|
||||
extern int protocol_version;
|
||||
extern int remove_source_files;
|
||||
@@ -116,7 +117,7 @@ static int active_filecnt = 0;
|
||||
static OFF_T active_bytecnt = 0;
|
||||
static int first_message = 1;
|
||||
|
||||
static char int_byte_extra[64] = {
|
||||
static const char int_byte_extra[64] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (00 - 3F)/4 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (40 - 7F)/4 */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (80 - BF)/4 */
|
||||
@@ -1089,6 +1090,9 @@ 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]);
|
||||
@@ -1157,8 +1161,8 @@ void set_io_timeout(int secs)
|
||||
|
||||
static void check_for_d_option_error(const char *msg)
|
||||
{
|
||||
static char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
|
||||
char *colon;
|
||||
static const char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
|
||||
const char *colon;
|
||||
int saw_d = 0;
|
||||
|
||||
if (*msg != 'r'
|
||||
@@ -1864,6 +1868,45 @@ int64 read_varlong(int f, uchar min_bytes)
|
||||
return u.x;
|
||||
}
|
||||
|
||||
/* Read an int32 and verify lo <= v <= hi. On out-of-range, abort with a
|
||||
* protocol error naming "what". The bound is co-located with the read so it
|
||||
* cannot be forgotten by a downstream user. */
|
||||
int32 read_int_bounded(int f, int32 lo, int32 hi, const char *what)
|
||||
{
|
||||
int32 v = read_int(f);
|
||||
if (v < lo || v > hi) {
|
||||
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
|
||||
what, (long)v, (long)lo, (long)hi, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* As read_int_bounded but for varint-encoded values. */
|
||||
int32 read_varint_bounded(int f, int32 lo, int32 hi, const char *what)
|
||||
{
|
||||
int32 v = read_varint(f);
|
||||
if (v < lo || v > hi) {
|
||||
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
|
||||
what, (long)v, (long)lo, (long)hi, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read a varint that will be used as a size_t. Rejects negative values
|
||||
* (which would wrap to ~SIZE_MAX) and values exceeding the supplied max. */
|
||||
size_t read_varint_size(int f, size_t max, const char *what)
|
||||
{
|
||||
int32 v = read_varint(f);
|
||||
if (v < 0 || (size_t)v > max) {
|
||||
rprintf(FERROR, "wire size %s out of range: %ld > %lu [%s]\n",
|
||||
what, (long)v, (unsigned long)max, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
return (size_t)v;
|
||||
}
|
||||
|
||||
int64 read_longint(int f)
|
||||
{
|
||||
#if SIZEOF_INT64 >= 8
|
||||
@@ -1970,6 +2013,21 @@ void read_sum_head(int f, struct sum_struct *sum)
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
/* Guard against integer overflow in downstream allocations sized by
|
||||
* count*element_size. my_alloc uses divide-not-multiply so it is
|
||||
* already wraparound-safe, but checking here gives a clearer error
|
||||
* and also covers the (size_t)count * xfer_sum_len arithmetic that
|
||||
* is performed *before* reaching my_alloc. */
|
||||
if (xfer_sum_len > 0 && (size_t)sum->count > SIZE_MAX / (size_t)xfer_sum_len) {
|
||||
rprintf(FERROR, "Invalid checksum count %ld (too large) [%s]\n",
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if ((size_t)sum->count > SIZE_MAX / sizeof(struct sum_buf)) {
|
||||
rprintf(FERROR, "Invalid checksum count %ld (sum_buf overflow) [%s]\n",
|
||||
(long)sum->count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
sum->blength = read_int(f);
|
||||
if (sum->blength < 0 || sum->blength > max_blength) {
|
||||
rprintf(FERROR, "Invalid block length %ld [%s]\n",
|
||||
@@ -1977,7 +2035,7 @@ void read_sum_head(int f, struct sum_struct *sum)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
|
||||
if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
|
||||
if (sum->s2length < 0 || sum->s2length > xfer_sum_len) {
|
||||
rprintf(FERROR, "Invalid checksum length %d [%s]\n",
|
||||
sum->s2length, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define LATEST_YEAR "2024"
|
||||
#define LATEST_YEAR "2026"
|
||||
|
||||
@@ -197,7 +197,7 @@ void md5_update(md_context *ctx, const uchar *input, uint32 length)
|
||||
memcpy(ctx->buffer + left, input, length);
|
||||
}
|
||||
|
||||
static uchar md5_padding[CSUM_CHUNK] = { 0x80 };
|
||||
static const uchar md5_padding[CSUM_CHUNK] = { 0x80 };
|
||||
|
||||
void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ struct alloc_pool
|
||||
size_t size; /* extent size */
|
||||
size_t quantum; /* allocation quantum */
|
||||
struct pool_extent *extents; /* top extent is "live" */
|
||||
void (*bomb)(); /* called if malloc fails */
|
||||
void (*bomb)(const char*, const char*, int); /* called if malloc fails */
|
||||
int flags;
|
||||
|
||||
/* statistical data */
|
||||
@@ -42,6 +42,7 @@ struct align_test {
|
||||
/* Temporarily cast a void* var into a char* var when adding an offset (to
|
||||
* keep some compilers from complaining about the pointer arithmetic). */
|
||||
#define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) )
|
||||
#define PTR_SUB(b,o) ( (void*) ((char*)(b) - (o)) )
|
||||
|
||||
alloc_pool_t
|
||||
pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags)
|
||||
@@ -100,7 +101,7 @@ pool_destroy(alloc_pool_t p)
|
||||
for (cur = pool->extents; cur; cur = next) {
|
||||
next = cur->next;
|
||||
if (pool->flags & POOL_PREPEND)
|
||||
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
|
||||
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
|
||||
else {
|
||||
free(cur->start);
|
||||
free(cur);
|
||||
@@ -235,7 +236,7 @@ pool_free(alloc_pool_t p, size_t len, void *addr)
|
||||
if (cur->free + cur->bound >= pool->size) {
|
||||
prev->next = cur->next;
|
||||
if (pool->flags & POOL_PREPEND)
|
||||
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
|
||||
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
|
||||
else {
|
||||
free(cur->start);
|
||||
free(cur);
|
||||
@@ -292,7 +293,7 @@ pool_free_old(alloc_pool_t p, void *addr)
|
||||
while ((cur = next) != NULL) {
|
||||
next = cur->next;
|
||||
if (pool->flags & POOL_PREPEND)
|
||||
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
|
||||
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
|
||||
else {
|
||||
free(cur->start);
|
||||
free(cur);
|
||||
|
||||
@@ -126,9 +126,18 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
|
||||
unsigned char keylen;
|
||||
ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
|
||||
|
||||
if (len <= 0 || (size_t)len > size)
|
||||
if (len <= 0 || size == 0)
|
||||
return len;
|
||||
|
||||
if ((size_t)len >= size) {
|
||||
/* FreeBSD extattr_list_xx() returns 'size' as 'len' in case there are
|
||||
more data available, truncating the output, we solve this by signalling
|
||||
ERANGE in case len == size so that the code in xattrs.c will retry with
|
||||
a bigger buffer */
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FreeBSD puts a single-byte length before each string, with no '\0'
|
||||
* terminator. We need to change this into a series of null-terminted
|
||||
* strings. Since the size is the same, we can simply transform the
|
||||
@@ -136,7 +145,7 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
|
||||
for (off = 0; off < len; off += keylen + 1) {
|
||||
keylen = ((unsigned char*)list)[off];
|
||||
if (off + keylen >= len) {
|
||||
/* Should be impossible, but kernel bugs happen! */
|
||||
/* Should be impossible, but bugs happen! */
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ typedef enum {
|
||||
|
||||
struct enum_list {
|
||||
int value;
|
||||
char *name;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct parm_struct {
|
||||
@@ -73,7 +73,7 @@ struct parm_struct {
|
||||
parm_type type;
|
||||
parm_class class;
|
||||
void *ptr;
|
||||
struct enum_list *enum_list;
|
||||
const struct enum_list *enum_list;
|
||||
unsigned flags;
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ static item_list section_list = EMPTY_ITEM_LIST;
|
||||
static int iSectionIndex = -1;
|
||||
static BOOL bInGlobalSection = True;
|
||||
|
||||
static struct enum_list enum_syslog_facility[] = {
|
||||
static const struct enum_list enum_syslog_facility[] = {
|
||||
#ifdef LOG_AUTH
|
||||
{ LOG_AUTH, "auth" },
|
||||
#endif
|
||||
@@ -178,7 +178,7 @@ static char *expand_vars(const char *str)
|
||||
|
||||
for (t = buf, f = str; bufsize && *f; ) {
|
||||
if (*f == '%' && isUpper(f+1)) {
|
||||
char *percent = strchr(f+1, '%');
|
||||
const char *percent = strchr(f+1, '%');
|
||||
if (percent && percent - f < bufsize) {
|
||||
char *val;
|
||||
strlcpy(t, f+1, percent - f);
|
||||
|
||||
12
log.c
12
log.c
@@ -456,11 +456,17 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...)
|
||||
char buf[BIGPATHBUFLEN];
|
||||
size_t len;
|
||||
|
||||
/* snprintf returns the would-have-been length on truncation, so
|
||||
* each cumulative call must be guarded; if not, sizeof buf - len
|
||||
* can underflow when promoted to size_t and the next call writes
|
||||
* past the buffer. */
|
||||
len = snprintf(buf, sizeof buf, RSYNC_NAME ": [%s] ", who_am_i());
|
||||
|
||||
va_start(ap, format);
|
||||
len += vsnprintf(buf + len, sizeof buf - len, format, ap);
|
||||
va_end(ap);
|
||||
if (len < sizeof buf) {
|
||||
va_start(ap, format);
|
||||
len += vsnprintf(buf + len, sizeof buf - len, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
if (len < sizeof buf) {
|
||||
len += snprintf(buf + len, sizeof buf - len,
|
||||
|
||||
36
main.c
36
main.c
@@ -66,7 +66,7 @@ extern int protect_args;
|
||||
extern int relative_paths;
|
||||
extern int sanitize_paths;
|
||||
extern int curr_dir_depth;
|
||||
extern int curr_dir_len;
|
||||
extern unsigned int curr_dir_len;
|
||||
extern int module_id;
|
||||
extern int rsync_port;
|
||||
extern int whole_file;
|
||||
@@ -239,11 +239,11 @@ void write_del_stats(int f)
|
||||
|
||||
void read_del_stats(int f)
|
||||
{
|
||||
stats.deleted_files = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_dirs = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_symlinks = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_devices = read_varint(f);
|
||||
stats.deleted_files += stats.deleted_specials = read_varint(f);
|
||||
stats.deleted_files = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_files");
|
||||
stats.deleted_files += stats.deleted_dirs = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_dirs");
|
||||
stats.deleted_files += stats.deleted_symlinks = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_symlinks");
|
||||
stats.deleted_files += stats.deleted_devices = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_devices");
|
||||
stats.deleted_files += stats.deleted_specials = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_specials");
|
||||
}
|
||||
|
||||
static void become_copy_as_user()
|
||||
@@ -386,7 +386,7 @@ static void handle_stats(int f)
|
||||
|
||||
static void output_itemized_counts(const char *prefix, int *counts)
|
||||
{
|
||||
static char *labels[] = { "reg", "dir", "link", "dev", "special" };
|
||||
static char *const labels[] = { "reg", "dir", "link", "dev", "special" };
|
||||
char buf[1024], *pre = " (";
|
||||
int j, len = 0;
|
||||
int total = counts[0];
|
||||
@@ -394,9 +394,18 @@ static void output_itemized_counts(const char *prefix, int *counts)
|
||||
counts[0] -= counts[1] + counts[2] + counts[3] + counts[4];
|
||||
for (j = 0; j < 5; j++) {
|
||||
if (counts[j]) {
|
||||
/* snprintf can return more than its size arg
|
||||
* on truncation; keep len <= sizeof buf - 2 so
|
||||
* the closing ')' and trailing NUL always
|
||||
* have room and the next iteration's
|
||||
* sizeof buf - len - 2 cannot underflow. */
|
||||
if (len >= (int)sizeof buf - 2)
|
||||
break;
|
||||
len += snprintf(buf+len, sizeof buf - len - 2,
|
||||
"%s%s: %s",
|
||||
pre, labels[j], comma_num(counts[j]));
|
||||
if (len > (int)sizeof buf - 2)
|
||||
len = (int)sizeof buf - 2;
|
||||
pre = ", ";
|
||||
}
|
||||
}
|
||||
@@ -1559,6 +1568,10 @@ static int start_client(int argc, char *argv[])
|
||||
shell_user = shell_machine;
|
||||
shell_machine = p+1;
|
||||
}
|
||||
if (*shell_machine == '-') {
|
||||
rprintf(FERROR, "Invalid remote host: hostnames may not start with '-'.\n");
|
||||
exit_cleanup(RERR_SYNTAX);
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_GTE(CMD, 2)) {
|
||||
@@ -1605,6 +1618,11 @@ static void sigusr2_handler(UNUSED(int val))
|
||||
if (!am_server)
|
||||
output_summary();
|
||||
close_all();
|
||||
#ifdef GCOV_COVERAGE
|
||||
/* The receiver child is killed here via SIGUSR2 and exits with _exit(),
|
||||
* bypassing the gcov atexit flush; without this it writes no .gcda. */
|
||||
{ extern void __gcov_dump(void); __gcov_dump(); }
|
||||
#endif
|
||||
if (got_xfer_error)
|
||||
_exit(RERR_PARTIAL);
|
||||
_exit(0);
|
||||
@@ -1743,7 +1761,9 @@ int main(int argc,char *argv[])
|
||||
our_gid = MY_GID();
|
||||
am_root = our_uid == ROOT_UID;
|
||||
|
||||
unset_env_var("DISPLAY");
|
||||
// DISPLAY should not be emptied unconditionally
|
||||
if (!getenv("SSH_ASKPASS"))
|
||||
unset_env_var("DISPLAY");
|
||||
|
||||
#if defined USE_OPENSSL && defined SET_OPENSSL_CONF
|
||||
#define TO_STR2(x) #x
|
||||
|
||||
11
match.c
11
match.c
@@ -147,6 +147,9 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
int more;
|
||||
schar *map;
|
||||
|
||||
// prevent possible memory leaks
|
||||
memset(sum2, 0, sizeof sum2);
|
||||
|
||||
/* want_i is used to encourage adjacent matches, allowing the RLL
|
||||
* coding of the output to work more efficiently. */
|
||||
want_i = 0;
|
||||
@@ -232,7 +235,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
done_csum2 = 1;
|
||||
}
|
||||
|
||||
if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) {
|
||||
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0) {
|
||||
false_alarms++;
|
||||
continue;
|
||||
}
|
||||
@@ -252,7 +255,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
if (i != aligned_i) {
|
||||
if (sum != s->sums[aligned_i].sum1
|
||||
|| l != s->sums[aligned_i].len
|
||||
|| memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0)
|
||||
|| memcmp(sum2, sum2_at(s, aligned_i), s->s2length) != 0)
|
||||
goto check_want_i;
|
||||
i = aligned_i;
|
||||
}
|
||||
@@ -271,7 +274,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
if (sum != s->sums[i].sum1)
|
||||
goto check_want_i;
|
||||
get_checksum2((char *)map, l, sum2);
|
||||
if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0)
|
||||
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0)
|
||||
goto check_want_i;
|
||||
/* OK, we have a re-alignment match. Bump the offset
|
||||
* forward to the new match point. */
|
||||
@@ -290,7 +293,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
&& (!updating_basis_file || s->sums[want_i].offset >= offset
|
||||
|| s->sums[want_i].flags & SUMFLG_SAME_OFFSET)
|
||||
&& sum == s->sums[want_i].sum1
|
||||
&& memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) {
|
||||
&& memcmp(sum2, sum2_at(s, want_i), s->s2length) == 0) {
|
||||
/* we've found an adjacent match - the RLL coder
|
||||
* will be happy */
|
||||
i = want_i;
|
||||
|
||||
@@ -120,6 +120,7 @@ TZ_RE = re.compile(r'^#define\s+MAINTAINER_TZ_OFFSET\s+(-?\d+(\.\d+)?)', re.M)
|
||||
VAR_REF_RE = re.compile(r'\$\{(\w+)\}')
|
||||
VERSION_RE = re.compile(r' (\d[.\d]+)[, ]')
|
||||
BIN_CHARS_RE = re.compile(r'[\1-\7]+')
|
||||
LONG_OPT_DASH_RE = re.compile(r'(--\w[-\w]+)')
|
||||
SPACE_DOUBLE_DASH_RE = re.compile(r'\s--(\s)')
|
||||
NON_SPACE_SINGLE_DASH_RE = re.compile(r'(^|\W)-')
|
||||
WHITESPACE_RE = re.compile(r'\s')
|
||||
@@ -247,6 +248,9 @@ def find_man_substitutions():
|
||||
|
||||
env_subs['date'] = time.strftime('%d %b %Y', time.gmtime(mtime + tz_offset)).lstrip('0')
|
||||
|
||||
if 'SOURCE_DATE_EPOCH' in os.environ:
|
||||
env_subs['date'] = time.strftime('%d %b %Y', time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
|
||||
|
||||
|
||||
def html_via_commonmark(txt):
|
||||
return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
|
||||
@@ -540,6 +544,7 @@ class TransformHtml(HTMLParser):
|
||||
if st.in_pre:
|
||||
html = htmlify(txt)
|
||||
else:
|
||||
txt = LONG_OPT_DASH_RE.sub(lambda x: x.group(1).replace('-', NBR_DASH[0]), txt)
|
||||
txt = SPACE_DOUBLE_DASH_RE.sub(NBR_SPACE[0] + r'--\1', txt).replace('--', NBR_DASH[0]*2)
|
||||
txt = NON_SPACE_SINGLE_DASH_RE.sub(r'\1' + NBR_DASH[0], txt)
|
||||
html = htmlify(txt)
|
||||
|
||||
30
options.c
30
options.c
@@ -86,6 +86,7 @@ int sparse_files = 0;
|
||||
int preallocate_files = 0;
|
||||
int do_compression = 0;
|
||||
int do_compression_level = CLVL_NOT_SPECIFIED;
|
||||
int do_compression_threads = 0; /*n = 0 use rsync thread, n >= 1 spawn n threads for compression */
|
||||
int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
|
||||
int am_server = 0;
|
||||
int am_sender = 0;
|
||||
@@ -113,11 +114,20 @@ 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;
|
||||
@@ -225,7 +235,7 @@ char *iconv_opt =
|
||||
|
||||
struct chmod_mode_struct *chmod_modes = NULL;
|
||||
|
||||
static const char *debug_verbosity[] = {
|
||||
static const char *const debug_verbosity[] = {
|
||||
/*0*/ NULL,
|
||||
/*1*/ NULL,
|
||||
/*2*/ "BIND,CMD,CONNECT,DEL,DELTASUM,DUP,FILTER,FLIST,ICONV",
|
||||
@@ -236,7 +246,7 @@ static const char *debug_verbosity[] = {
|
||||
|
||||
#define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1)
|
||||
|
||||
static const char *info_verbosity[1+MAX_VERBOSITY] = {
|
||||
static const char *const info_verbosity[1+MAX_VERBOSITY] = {
|
||||
/*0*/ "NONREG",
|
||||
/*1*/ "COPY,DEL,FLIST,MISC,NAME,STATS,SYMSAFE",
|
||||
/*2*/ "BACKUP,MISC2,MOUNT,NAME2,REMOVE,SKIP",
|
||||
@@ -474,7 +484,7 @@ static void parse_output_words(struct output_struct *words, short *levels, const
|
||||
static void output_item_help(struct output_struct *words)
|
||||
{
|
||||
short *levels = words == info_words ? info_levels : debug_levels;
|
||||
const char **verbosity = words == info_words ? info_verbosity : debug_verbosity;
|
||||
const char *const*verbosity = words == info_words ? info_verbosity : debug_verbosity;
|
||||
char buf[128], *opt, *fmt = "%-10s %s\n";
|
||||
int j;
|
||||
|
||||
@@ -756,6 +766,8 @@ static struct poptOption long_options[] = {
|
||||
{"skip-compress", 0, POPT_ARG_STRING, &skip_compress, 0, 0, 0 },
|
||||
{"compress-level", 0, POPT_ARG_INT, &do_compression_level, 0, 0, 0 },
|
||||
{"zl", 0, POPT_ARG_INT, &do_compression_level, 0, 0, 0 },
|
||||
{"compress-threads", 0, POPT_ARG_INT, &do_compression_threads, 0, 0, 0 },
|
||||
{"zt", 0, POPT_ARG_INT, &do_compression_threads, 0, 0, 0 },
|
||||
{0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
|
||||
{"progress", 0, POPT_ARG_VAL, &do_progress, 1, 0, 0 },
|
||||
{"no-progress", 0, POPT_ARG_VAL, &do_progress, 0, 0, 0 },
|
||||
@@ -844,7 +856,7 @@ static struct poptOption long_options[] = {
|
||||
{0,0,0,0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static struct poptOption long_daemon_options[] = {
|
||||
static const struct poptOption long_daemon_options[] = {
|
||||
/* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
|
||||
{"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
|
||||
{"bwlimit", 0, POPT_ARG_INT, &daemon_bwlimit, 0, 0, 0 },
|
||||
@@ -1156,7 +1168,7 @@ static time_t parse_time(const char *arg)
|
||||
{
|
||||
const char *cp;
|
||||
time_t val, now = time(NULL);
|
||||
struct tm t, *today = localtime(&now);
|
||||
struct tm t, tmp, *today = localtime_r(&now, &tmp);
|
||||
int in_date, old_mday, n;
|
||||
|
||||
memset(&t, 0, sizeof t);
|
||||
@@ -1369,6 +1381,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
/* TODO: Call poptReadDefaultConfig; handle errors. */
|
||||
|
||||
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");
|
||||
@@ -2006,6 +2022,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
create_refuse_error(refused_compress);
|
||||
goto cleanup;
|
||||
}
|
||||
if (do_compression_threads < 0)
|
||||
do_compression_threads = 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETVBUF
|
||||
@@ -2563,7 +2581,7 @@ char *safe_arg(const char *opt, const char *arg)
|
||||
if (escape_leading_tilde)
|
||||
*t++ = '\\';
|
||||
while (*f) {
|
||||
if (*f == '\\') {
|
||||
if (*f == '\\') {
|
||||
if (!is_filename_arg || !strchr(WILD_CHARS, f[1]))
|
||||
*t++ = '\\';
|
||||
} else if (strchr(escapes, *f))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
TARGETS := all install install-ssl-daemon install-all install-strip conf gen gensend reconfigure restatus \
|
||||
TARGETS := all install install-ssl-daemon install-all install-strip conf gen reconfigure restatus \
|
||||
proto man clean cleantests distclean test check check29 check30 installcheck splint \
|
||||
doxygen doxygen-upload finddead rrsync
|
||||
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/usr/bin/env -S python3 -B
|
||||
|
||||
# This script turns one or more diff files in the patches dir (which is
|
||||
# expected to be a checkout of the rsync-patches git repo) into a branch
|
||||
# in the main rsync git checkout. This allows the applied patch to be
|
||||
# merged with the latest rsync changes and tested. To update the diff
|
||||
# with the resulting changes, see the patch-update script.
|
||||
|
||||
import os, sys, re, argparse, glob
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import *
|
||||
|
||||
def main():
|
||||
global created, info, local_branch
|
||||
|
||||
cur_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
|
||||
|
||||
local_branch = get_patch_branches(args.base_branch)
|
||||
|
||||
if args.delete_local_branches:
|
||||
for name in sorted(local_branch):
|
||||
branch = f"patch/{args.base_branch}/{name}"
|
||||
cmd_chk(['git', 'branch', '-D', branch])
|
||||
local_branch = set()
|
||||
|
||||
if args.add_missing:
|
||||
for fn in sorted(glob.glob(f"{args.patches_dir}/*.diff")):
|
||||
name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
|
||||
if name not in local_branch and fn not in args.patch_files:
|
||||
args.patch_files.append(fn)
|
||||
|
||||
if not args.patch_files:
|
||||
return
|
||||
|
||||
for fn in args.patch_files:
|
||||
if not fn.endswith('.diff'):
|
||||
die(f"Filename is not a .diff file: {fn}")
|
||||
if not os.path.isfile(fn):
|
||||
die(f"File not found: {fn}")
|
||||
|
||||
scanned = set()
|
||||
info = { }
|
||||
|
||||
patch_list = [ ]
|
||||
for fn in args.patch_files:
|
||||
m = re.match(r'^(?P<dir>.*?)(?P<name>[^/]+)\.diff$', fn)
|
||||
patch = argparse.Namespace(**m.groupdict())
|
||||
if patch.name in scanned:
|
||||
continue
|
||||
patch.fn = fn
|
||||
|
||||
lines = [ ]
|
||||
commit_hash = None
|
||||
with open(patch.fn, 'r', encoding='utf-8') as fh:
|
||||
for line in fh:
|
||||
m = re.match(r'^based-on: (\S+)', line)
|
||||
if m:
|
||||
commit_hash = m[1]
|
||||
break
|
||||
if (re.match(r'^index .*\.\..* \d', line)
|
||||
or re.match(r'^diff --git ', line)
|
||||
or re.match(r'^--- (old|a)/', line)):
|
||||
break
|
||||
lines.append(re.sub(r'\s*\Z', "\n", line, 1))
|
||||
info_txt = ''.join(lines).strip() + "\n"
|
||||
lines = None
|
||||
|
||||
parent = args.base_branch
|
||||
patches = re.findall(r'patch -p1 <%s/(\S+)\.diff' % args.patches_dir, info_txt)
|
||||
if patches:
|
||||
last = patches.pop()
|
||||
if last != patch.name:
|
||||
warn(f"No identity patch line in {patch.fn}")
|
||||
patches.append(last)
|
||||
if patches:
|
||||
parent = patches.pop()
|
||||
if parent not in scanned:
|
||||
diff_fn = patch.dir + parent + '.diff'
|
||||
if not os.path.isfile(diff_fn):
|
||||
die(f"Failed to find parent of {patch.fn}: {parent}")
|
||||
# Add parent to args.patch_files so that we will look for the
|
||||
# parent's parent. Any duplicates will be ignored.
|
||||
args.patch_files.append(diff_fn)
|
||||
else:
|
||||
warn(f"No patch lines found in {patch.fn}")
|
||||
|
||||
info[patch.name] = [ parent, info_txt, commit_hash ]
|
||||
|
||||
patch_list.append(patch)
|
||||
|
||||
created = set()
|
||||
for patch in patch_list:
|
||||
create_branch(patch)
|
||||
|
||||
cmd_chk(['git', 'checkout', args.base_branch])
|
||||
|
||||
|
||||
def create_branch(patch):
|
||||
if patch.name in created:
|
||||
return
|
||||
created.add(patch.name)
|
||||
|
||||
parent, info_txt, commit_hash = info[patch.name]
|
||||
parent = argparse.Namespace(dir=patch.dir, name=parent, fn=patch.dir + parent + '.diff')
|
||||
|
||||
if parent.name == args.base_branch:
|
||||
parent_branch = commit_hash if commit_hash else args.base_branch
|
||||
else:
|
||||
create_branch(parent)
|
||||
parent_branch = '/'.join(['patch', args.base_branch, parent.name])
|
||||
|
||||
branch = '/'.join(['patch', args.base_branch, patch.name])
|
||||
print("\n" + '=' * 64)
|
||||
print(f"Processing {branch} ({parent_branch})")
|
||||
|
||||
if patch.name in local_branch:
|
||||
cmd_chk(['git', 'branch', '-D', branch])
|
||||
|
||||
cmd_chk(['git', 'checkout', '-b', branch, parent_branch])
|
||||
|
||||
info_fn = 'PATCH.' + patch.name
|
||||
with open(info_fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(info_txt)
|
||||
cmd_chk(['git', 'add', info_fn])
|
||||
|
||||
with open(patch.fn, 'r', encoding='utf-8') as fh:
|
||||
patch_txt = fh.read()
|
||||
|
||||
cmd_run('patch -p1'.split(), input=patch_txt)
|
||||
|
||||
for fn in glob.glob('*.orig') + glob.glob('*/*.orig'):
|
||||
os.unlink(fn)
|
||||
|
||||
pos = 0
|
||||
new_file_re = re.compile(r'\nnew file mode (?P<mode>\d+)\s+--- /dev/null\s+\+\+\+ b/(?P<fn>.+)')
|
||||
while True:
|
||||
m = new_file_re.search(patch_txt, pos)
|
||||
if not m:
|
||||
break
|
||||
os.chmod(m['fn'], int(m['mode'], 8))
|
||||
cmd_chk(['git', 'add', m['fn']])
|
||||
pos = m.end()
|
||||
|
||||
while True:
|
||||
cmd_chk('git status'.split())
|
||||
ans = input('Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ')
|
||||
if ans == '':
|
||||
break
|
||||
cmd_chk("git add " + ans, shell=True)
|
||||
|
||||
while True:
|
||||
s = cmd_run(['git', 'commit', '-a', '-m', f"Creating branch from {patch.name}.diff."])
|
||||
if not s.returncode:
|
||||
break
|
||||
s = cmd_run(['/bin/zsh'])
|
||||
if s.returncode:
|
||||
die('Aborting due to shell error code')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Create a git patch branch from an rsync patch file.", add_help=False)
|
||||
parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.")
|
||||
parser.add_argument('--add-missing', '-a', action='store_true', help="Add a branch for every patches/*.diff that doesn't have a branch.")
|
||||
parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
|
||||
parser.add_argument('--delete', dest='delete_local_branches', action='store_true', help="Delete all the local patch/BASE/* branches, not just the ones that are being recreated.")
|
||||
parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.")
|
||||
parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
2
packaging/ftp.filt
Normal file
2
packaging/ftp.filt
Normal file
@@ -0,0 +1,2 @@
|
||||
- /generated-files/
|
||||
- /binaries/
|
||||
@@ -1,6 +1,6 @@
|
||||
Summary: A fast, versatile, remote (and local) file-copying tool
|
||||
Name: rsync
|
||||
Version: 3.3.0
|
||||
Version: 3.4.3
|
||||
%define fullversion %{version}
|
||||
Release: 1
|
||||
%define srcdir src
|
||||
@@ -79,9 +79,5 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%dir /etc/rsync-ssl/certs
|
||||
|
||||
%changelog
|
||||
* Sat Apr 06 2024 Wayne Davison <wayne@opencoder.net>
|
||||
Released 3.3.0.
|
||||
|
||||
* Fri Mar 21 2008 Wayne Davison <wayne@opencoder.net>
|
||||
Added installation of /etc/xinetd.d/rsync file and some commented-out
|
||||
lines that demonstrate how to use the rsync-patches tar file.
|
||||
* Wed May 20 2026 Rsync Project <rsync.project@gmail.com>
|
||||
Released 3.4.3.
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
#!/usr/bin/env -S python3 -B
|
||||
|
||||
# This script is used to turn one or more of the "patch/BASE/*" branches
|
||||
# into one or more diffs in the "patches" directory. Pass the option
|
||||
# --gen if you want generated files in the diffs. Pass the name of
|
||||
# one or more diffs if you want to just update a subset of all the
|
||||
# diffs.
|
||||
|
||||
import os, sys, re, argparse, time, shutil
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import *
|
||||
|
||||
MAKE_GEN_CMDS = [
|
||||
'./prepare-source'.split(),
|
||||
'cd build && if test -f config.status ; then ./config.status ; else ../configure ; fi',
|
||||
'make -C build gen'.split(),
|
||||
]
|
||||
TMP_DIR = "patches.gen"
|
||||
|
||||
os.environ['GIT_MERGE_AUTOEDIT'] = 'no'
|
||||
|
||||
def main():
|
||||
global master_commit, parent_patch, description, completed, last_touch
|
||||
|
||||
if not os.path.isdir(args.patches_dir):
|
||||
die(f'No "{args.patches_dir}" directory was found.')
|
||||
if not os.path.isdir('.git'):
|
||||
die('No ".git" directory present in the current dir.')
|
||||
|
||||
starting_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
|
||||
|
||||
master_commit = latest_git_hash(args.base_branch)
|
||||
|
||||
if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
|
||||
die('You must setup an auto-build-save dir to use this script.')
|
||||
|
||||
if args.gen:
|
||||
if os.path.lexists(TMP_DIR):
|
||||
die(f'"{TMP_DIR}" must not exist in the current directory.')
|
||||
gen_files = get_gen_files()
|
||||
os.mkdir(TMP_DIR, 0o700)
|
||||
for cmd in MAKE_GEN_CMDS:
|
||||
cmd_chk(cmd)
|
||||
cmd_chk(['rsync', '-a', *gen_files, f'{TMP_DIR}/master/'])
|
||||
|
||||
last_touch = int(time.time())
|
||||
|
||||
# Start by finding all patches so that we can load all possible parents.
|
||||
patches = sorted(list(get_patch_branches(args.base_branch)))
|
||||
|
||||
parent_patch = { }
|
||||
description = { }
|
||||
|
||||
for patch in patches:
|
||||
branch = f"patch/{args.base_branch}/{patch}"
|
||||
desc = ''
|
||||
proc = cmd_pipe(['git', 'diff', '-U1000', f"{args.base_branch}...{branch}", '--', f"PATCH.{patch}"])
|
||||
in_diff = False
|
||||
for line in proc.stdout:
|
||||
if in_diff:
|
||||
if not re.match(r'^[ +]', line):
|
||||
continue
|
||||
line = line[1:]
|
||||
m = re.search(r'patch -p1 <patches/(\S+)\.diff', line)
|
||||
if m and m[1] != patch:
|
||||
parpat = parent_patch[patch] = m[1]
|
||||
if not parpat in patches:
|
||||
die(f"Parent of {patch} is not a local branch: {parpat}")
|
||||
desc += line
|
||||
elif re.match(r'^@@ ', line):
|
||||
in_diff = True
|
||||
description[patch] = desc
|
||||
proc.communicate()
|
||||
|
||||
if args.patch_files: # Limit the list of patches to actually process
|
||||
valid_patches = patches
|
||||
patches = [ ]
|
||||
for fn in args.patch_files:
|
||||
name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
|
||||
if name not in valid_patches:
|
||||
die(f"Local branch not available for patch: {name}")
|
||||
patches.append(name)
|
||||
|
||||
completed = set()
|
||||
|
||||
for patch in patches:
|
||||
if patch in completed:
|
||||
continue
|
||||
if not update_patch(patch):
|
||||
break
|
||||
|
||||
if args.gen:
|
||||
shutil.rmtree(TMP_DIR)
|
||||
|
||||
while last_touch >= int(time.time()):
|
||||
time.sleep(1)
|
||||
cmd_chk(['git', 'checkout', starting_branch])
|
||||
cmd_chk(['packaging/prep-auto-dir'], discard='output')
|
||||
|
||||
|
||||
def update_patch(patch):
|
||||
global last_touch
|
||||
|
||||
completed.add(patch) # Mark it as completed early to short-circuit any (bogus) dependency loops.
|
||||
|
||||
parent = parent_patch.get(patch, None)
|
||||
if parent:
|
||||
if parent not in completed:
|
||||
if not update_patch(parent):
|
||||
return 0
|
||||
based_on = parent = f"patch/{args.base_branch}/{parent}"
|
||||
else:
|
||||
parent = args.base_branch
|
||||
based_on = master_commit
|
||||
|
||||
print(f"======== {patch} ========")
|
||||
|
||||
while args.gen and last_touch >= int(time.time()):
|
||||
time.sleep(1)
|
||||
|
||||
branch = f"patch/{args.base_branch}/{patch}"
|
||||
s = cmd_run(['git', 'checkout', branch])
|
||||
if s.returncode != 0:
|
||||
return 0
|
||||
|
||||
s = cmd_run(['git', 'merge', based_on])
|
||||
ok = s.returncode == 0
|
||||
skip_shell = False
|
||||
if not ok or args.cmd or args.make or args.shell:
|
||||
cmd_chk(['packaging/prep-auto-dir'], discard='output')
|
||||
if not ok:
|
||||
print(f'"git merge {based_on}" incomplete -- please fix.')
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
if not args.make and not args.cmd:
|
||||
skip_shell = True
|
||||
if args.make:
|
||||
if cmd_run(['packaging/smart-make']).returncode != 0:
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
if not args.cmd:
|
||||
skip_shell = True
|
||||
if args.cmd:
|
||||
if cmd_run(args.cmd).returncode != 0:
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
skip_shell = True
|
||||
if args.shell and not skip_shell:
|
||||
if not run_a_shell(parent, patch):
|
||||
return 0
|
||||
|
||||
with open(f"{args.patches_dir}/{patch}.diff", 'w', encoding='utf-8') as fh:
|
||||
fh.write(description[patch])
|
||||
fh.write(f"\nbased-on: {based_on}\n")
|
||||
|
||||
if args.gen:
|
||||
gen_files = get_gen_files()
|
||||
for cmd in MAKE_GEN_CMDS:
|
||||
cmd_chk(cmd)
|
||||
cmd_chk(['rsync', '-a', *gen_files, f"{TMP_DIR}/{patch}/"])
|
||||
else:
|
||||
gen_files = [ ]
|
||||
last_touch = int(time.time())
|
||||
|
||||
proc = cmd_pipe(['git', 'diff', based_on])
|
||||
skipping = False
|
||||
for line in proc.stdout:
|
||||
if skipping:
|
||||
if not re.match(r'^diff --git a/', line):
|
||||
continue
|
||||
skipping = False
|
||||
elif re.match(r'^diff --git a/PATCH', line):
|
||||
skipping = True
|
||||
continue
|
||||
if not re.match(r'^index ', line):
|
||||
fh.write(line)
|
||||
proc.communicate()
|
||||
|
||||
if args.gen:
|
||||
e_tmp_dir = re.escape(TMP_DIR)
|
||||
diff_re = re.compile(r'^(diff -Nurp) %s/[^/]+/(.*?) %s/[^/]+/(.*)' % (e_tmp_dir, e_tmp_dir))
|
||||
minus_re = re.compile(r'^\-\-\- %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
|
||||
plus_re = re.compile(r'^\+\+\+ %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
|
||||
|
||||
if parent == args.base_branch:
|
||||
parent_dir = 'master'
|
||||
else:
|
||||
m = re.search(r'([^/]+)$', parent)
|
||||
parent_dir = m[1]
|
||||
|
||||
proc = cmd_pipe(['diff', '-Nurp', f"{TMP_DIR}/{parent_dir}", f"{TMP_DIR}/{patch}"])
|
||||
for line in proc.stdout:
|
||||
line = diff_re.sub(r'\1 a/\2 b/\3', line)
|
||||
line = minus_re.sub(r'--- a/\1', line)
|
||||
line = plus_re.sub(r'+++ b/\1', line)
|
||||
fh.write(line)
|
||||
proc.communicate()
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def run_a_shell(parent, patch):
|
||||
m = re.search(r'([^/]+)$', parent)
|
||||
parent_dir = m[1]
|
||||
os.environ['PS1'] = f"[{parent_dir}] {patch}: "
|
||||
|
||||
while True:
|
||||
s = cmd_run([os.environ.get('SHELL', '/bin/sh')])
|
||||
if s.returncode != 0:
|
||||
ans = input("Abort? [n/y] ")
|
||||
if re.match(r'^y', ans, flags=re.I):
|
||||
return False
|
||||
continue
|
||||
cur_branch, is_clean, status_txt = check_git_status(0)
|
||||
if is_clean:
|
||||
break
|
||||
print(status_txt, end='')
|
||||
|
||||
cmd_run('rm -f build/*.o build/*/*.o')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Turn a git branch back into a diff files in the patches dir.", add_help=False)
|
||||
parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.")
|
||||
parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
|
||||
parser.add_argument('--make', '-m', action='store_true', help="Run the smart-make script in every patch branch.")
|
||||
parser.add_argument('--cmd', '-c', help="Run a command in every patch branch.")
|
||||
parser.add_argument('--shell', '-s', action='store_true', help="Launch a shell for every patch/BASE/* branch updated, not just when a conflict occurs.")
|
||||
parser.add_argument('--gen', metavar='DIR', nargs='?', const='', help='Include generated files. Optional DIR value overrides the default of using the "patches" dir.')
|
||||
parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.")
|
||||
parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
if args.gen == '':
|
||||
args.gen = args.patches_dir
|
||||
elif args.gen is not None:
|
||||
args.patches_dir = args.gen
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
@@ -170,17 +170,6 @@ def get_patch_branches(base_branch):
|
||||
return branches
|
||||
|
||||
|
||||
def mandate_gensend_hook():
|
||||
hook = '.git/hooks/pre-push'
|
||||
if not os.path.exists(hook):
|
||||
print('Creating hook file:', hook)
|
||||
cmd_chk(['./rsync', '-a', 'packaging/pre-push', hook])
|
||||
else:
|
||||
ct = cmd_txt(['grep', 'make gensend', hook], discard='output')
|
||||
if ct.rc:
|
||||
die('Please add a "make gensend" into your', hook, 'script.')
|
||||
|
||||
|
||||
# Snag the GENFILES values out of the Makefile file and return them as a list.
|
||||
def get_gen_files(want_dir_plus_list=False):
|
||||
cont_re = re.compile(r'\\\n')
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
cat >/dev/null # Just discard stdin data
|
||||
|
||||
if [[ -f /proc/$PPID/cmdline ]]; then
|
||||
while read -d $'\0' arg ; do
|
||||
if [[ "$arg" == '--tags' ]] ; then
|
||||
exit 0
|
||||
fi
|
||||
done </proc/$PPID/cmdline
|
||||
fi
|
||||
|
||||
branch=`git rev-parse --abbrev-ref HEAD`
|
||||
if [[ "$branch" = master && "$*" == *github* ]]; then
|
||||
make gensend
|
||||
fi
|
||||
@@ -1,399 +0,0 @@
|
||||
#!/usr/bin/env -S python3 -B
|
||||
|
||||
# This script expects the directory ~/samba-rsync-ftp to exist and to be a
|
||||
# copy of the /home/ftp/pub/rsync dir on samba.org. When the script is done,
|
||||
# the git repository in the current directory will be updated, and the local
|
||||
# ~/samba-rsync-ftp dir will be ready to be rsynced to samba.org.
|
||||
|
||||
import os, sys, re, argparse, glob, shutil, signal
|
||||
from datetime import datetime
|
||||
from getpass import getpass
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import *
|
||||
|
||||
os.environ['LESS'] = 'mqeiXR'; # Make sure that -F is turned off and -R is turned on.
|
||||
dest = os.environ['HOME'] + '/samba-rsync-ftp'
|
||||
ORIGINAL_PATH = os.environ['PATH']
|
||||
|
||||
def main():
|
||||
if not os.path.isfile('packaging/release-rsync'):
|
||||
die('You must run this script from the top of your rsync checkout.')
|
||||
|
||||
now = datetime.now()
|
||||
cl_today = now.strftime('* %a %b %d %Y')
|
||||
year = now.strftime('%Y')
|
||||
ztoday = now.strftime('%d %b %Y')
|
||||
today = ztoday.lstrip('0')
|
||||
|
||||
mandate_gensend_hook()
|
||||
|
||||
curdir = os.getcwd()
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
|
||||
die('You must setup an auto-build-save dir to use this script.');
|
||||
|
||||
auto_dir, gen_files = get_gen_files(True)
|
||||
gen_pathnames = [ os.path.join(auto_dir, fn) for fn in gen_files ]
|
||||
|
||||
dash_line = '=' * 74
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
== This will release a new version of rsync onto an unsuspecting world. ==
|
||||
{dash_line}
|
||||
""")
|
||||
|
||||
with open('build/rsync.1') as fh:
|
||||
for line in fh:
|
||||
if line.startswith(r'.\" prefix='):
|
||||
doc_prefix = line.split('=')[1].strip()
|
||||
if doc_prefix != '/usr':
|
||||
warn(f"*** The documentation was built with prefix {doc_prefix} instead of /usr ***")
|
||||
die("*** Read the md2man script for a way to override this. ***")
|
||||
break
|
||||
if line.startswith('.P'):
|
||||
die("Failed to find the prefix comment at the start of the rsync.1 manpage.")
|
||||
|
||||
if not os.path.isdir(dest):
|
||||
die(dest, "dest does not exist")
|
||||
if not os.path.isdir('.git'):
|
||||
die("There is no .git dir in the current directory.")
|
||||
if os.path.lexists('a'):
|
||||
die('"a" must not exist in the current directory.')
|
||||
if os.path.lexists('b'):
|
||||
die('"b" must not exist in the current directory.')
|
||||
if os.path.lexists('patches.gen'):
|
||||
die('"patches.gen" must not exist in the current directory.')
|
||||
|
||||
check_git_state(args.master_branch, True, 'patches')
|
||||
|
||||
curversion = get_rsync_version()
|
||||
|
||||
# All version values are strings!
|
||||
lastversion, last_protocol_version, pdate = get_NEWS_version_info()
|
||||
protocol_version, subprotocol_version = get_protocol_versions()
|
||||
|
||||
version = curversion
|
||||
m = re.search(r'pre(\d+)', version)
|
||||
if m:
|
||||
version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version)
|
||||
else:
|
||||
version = version.replace('dev', 'pre1')
|
||||
|
||||
ans = input(f"Please enter the version number of this release: [{version}] ")
|
||||
if ans == '.':
|
||||
version = re.sub(r'pre\d+', '', version)
|
||||
elif ans != '':
|
||||
version = ans
|
||||
if not re.match(r'^[\d.]+(pre\d+)?$', version):
|
||||
die(f'Invalid version: "{version}"')
|
||||
|
||||
v_ver = 'v' + version
|
||||
rsync_ver = 'rsync-' + version
|
||||
|
||||
if os.path.lexists(rsync_ver):
|
||||
die(f'"{rsync_ver}" must not exist in the current directory.')
|
||||
|
||||
out = cmd_txt_chk(['git', 'tag', '-l', v_ver]).out
|
||||
if out != '':
|
||||
print(f"Tag {v_ver} already exists.")
|
||||
ans = input("\nDelete tag or quit? [Q/del] ")
|
||||
if not re.match(r'^del', ans, flags=re.I):
|
||||
die("Aborted")
|
||||
cmd_chk(['git', 'tag', '-d', v_ver])
|
||||
if os.path.isdir('patches/.git'):
|
||||
cmd_chk(f"cd patches && git tag -d '{v_ver}'")
|
||||
|
||||
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
|
||||
if 'pre' in version and not curversion.endswith('dev'):
|
||||
lastversion = curversion
|
||||
|
||||
ans = input(f"Enter the previous version to produce a patch against: [{lastversion}] ")
|
||||
if ans != '':
|
||||
lastversion = ans
|
||||
lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion)
|
||||
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
if os.path.lexists(rsync_lastver):
|
||||
die(f'"{rsync_lastver}" must not exist in the current directory.')
|
||||
|
||||
m = re.search(r'(pre\d+)', version)
|
||||
pre = m[1] if m else ''
|
||||
|
||||
release = '0.1' if pre else '1'
|
||||
ans = input(f"Please enter the RPM release number of this release: [{release}] ")
|
||||
if ans != '':
|
||||
release = ans
|
||||
if pre:
|
||||
release += '.' + pre
|
||||
|
||||
finalversion = re.sub(r'pre\d+', '', version)
|
||||
proto_changed = protocol_version != last_protocol_version
|
||||
if proto_changed:
|
||||
if finalversion in pdate:
|
||||
proto_change_date = pdate[finalversion]
|
||||
else:
|
||||
while True:
|
||||
ans = input("On what date did the protocol change to {protocol_version} get checked in? (dd Mmm yyyy) ")
|
||||
if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans):
|
||||
break
|
||||
proto_change_date = ans
|
||||
else:
|
||||
proto_change_date = ' ' * 11
|
||||
|
||||
if 'pre' in lastversion:
|
||||
if not pre:
|
||||
die("You should not diff a release version against a pre-release version.")
|
||||
srcdir = srcdiffdir = lastsrcdir = 'src-previews'
|
||||
skipping = ' ** SKIPPING **'
|
||||
elif pre:
|
||||
srcdir = srcdiffdir = 'src-previews'
|
||||
lastsrcdir = 'src'
|
||||
skipping = ' ** SKIPPING **'
|
||||
else:
|
||||
srcdir = lastsrcdir = 'src'
|
||||
srcdiffdir = 'src-diffs'
|
||||
skipping = ''
|
||||
|
||||
print(f"""
|
||||
{dash_line}
|
||||
version is "{version}"
|
||||
lastversion is "{lastversion}"
|
||||
dest is "{dest}"
|
||||
curdir is "{curdir}"
|
||||
srcdir is "{srcdir}"
|
||||
srcdiffdir is "{srcdiffdir}"
|
||||
lastsrcdir is "{lastsrcdir}"
|
||||
release is "{release}"
|
||||
|
||||
About to:
|
||||
- tweak SUBPROTOCOL_VERSION in rsync.h, if needed
|
||||
- tweak the version in version.h and the spec files
|
||||
- tweak NEWS.md to ensure header values are correct
|
||||
- generate configure.sh, config.h.in, and proto.h
|
||||
- page through the differences
|
||||
""")
|
||||
ans = input("<Press Enter to continue> ")
|
||||
|
||||
specvars = {
|
||||
'Version:': finalversion,
|
||||
'Release:': release,
|
||||
'%define fullversion': f'%{{version}}{pre}',
|
||||
'Released': version + '.',
|
||||
'%define srcdir': srcdir,
|
||||
}
|
||||
|
||||
tweak_files = 'version.h rsync.h NEWS.md'.split()
|
||||
tweak_files += glob.glob('packaging/*.spec')
|
||||
tweak_files += glob.glob('packaging/*/*.spec')
|
||||
|
||||
for fn in tweak_files:
|
||||
with open(fn, 'r', encoding='utf-8') as fh:
|
||||
old_txt = txt = fh.read()
|
||||
if fn == 'version.h':
|
||||
x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M)
|
||||
msg = f"Unable to update RSYNC_VERSION in {fn}"
|
||||
txt = replace_or_die(x_re, r'\1 "%s"' % version, txt, msg)
|
||||
elif '.spec' in fn:
|
||||
for var, val in specvars.items():
|
||||
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
|
||||
txt = replace_or_die(x_re, var + ' ' + val, txt, f"Unable to update {var} in {fn}")
|
||||
x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
|
||||
txt = replace_or_die(x_re, r'%s \1' % cl_today, txt, f"Unable to update ChangeLog header in {fn}")
|
||||
elif fn == 'rsync.h':
|
||||
x_re = re.compile('(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
|
||||
repl = lambda m: m[1] + ' ' + ('0' if not pre or not proto_changed else '1' if m[2] == '0' else m[2])
|
||||
txt = replace_or_die(x_re, repl, txt, f"Unable to find SUBPROTOCOL_VERSION define in {fn}")
|
||||
elif fn == 'NEWS.md':
|
||||
efv = re.escape(finalversion)
|
||||
x_re = re.compile(r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
|
||||
+ r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?')
|
||||
rel_day = 'UNRELEASED' if pre else today
|
||||
repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n'
|
||||
+ '## Changes in this version:\n')
|
||||
if proto_changed:
|
||||
repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n'
|
||||
good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1)
|
||||
msg = f"The top lines of {fn} are not in the right format. It should be:\n" + good_top
|
||||
txt = replace_or_die(x_re, repl, txt, msg)
|
||||
x_re = re.compile(r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv, re.M)
|
||||
repl = lambda m: m[1] + (m[2] if pre else ztoday) + m[3] + proto_change_date + m[4] + protocol_version + m[5]
|
||||
txt = replace_or_die(x_re, repl, txt, f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}')
|
||||
else:
|
||||
die(f"Unrecognized file in tweak_files: {fn}")
|
||||
|
||||
if txt != old_txt:
|
||||
print(f"Updating {fn}")
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(txt)
|
||||
|
||||
cmd_chk(['packaging/year-tweak'])
|
||||
|
||||
print(dash_line)
|
||||
cmd_run("git diff".split())
|
||||
|
||||
srctar_name = f"{rsync_ver}.tar.gz"
|
||||
pattar_name = f"rsync-patches-{version}.tar.gz"
|
||||
diff_name = f"{rsync_lastver}-{version}.diffs.gz"
|
||||
srctar_file = os.path.join(dest, srcdir, srctar_name)
|
||||
pattar_file = os.path.join(dest, srcdir, pattar_name)
|
||||
diff_file = os.path.join(dest, srcdiffdir, diff_name)
|
||||
lasttar_file = os.path.join(dest, lastsrcdir, rsync_lastver + '.tar.gz')
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
|
||||
About to:
|
||||
- git commit all changes
|
||||
- run a full build, ensuring that the manpages & configure.sh are up-to-date
|
||||
- merge the {args.master_branch} branch into the patch/{args.master_branch}/* branches
|
||||
- update the files in the "patches" dir and OPTIONALLY (if you type 'y') to
|
||||
run patch-update with the --make option (which opens a shell on error)
|
||||
""")
|
||||
ans = input("<Press Enter OR 'y' to continue> ")
|
||||
|
||||
s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]'])
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
cmd_chk('touch configure.ac && packaging/smart-make && make gen')
|
||||
|
||||
print('Creating any missing patch branches.')
|
||||
s = cmd_run(f'packaging/branch-from-patch --branch={args.master_branch} --add-missing')
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
print('Updating files in "patches" dir ...')
|
||||
s = cmd_run(f'packaging/patch-update --branch={args.master_branch}')
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
if re.match(r'^y', ans, re.I):
|
||||
print(f'\nRunning smart-make on all "patch/{args.master_branch}/*" branches ...')
|
||||
cmd_run(f"packaging/patch-update --branch={args.master_branch} --skip-check --make")
|
||||
|
||||
if os.path.isdir('patches/.git'):
|
||||
s = cmd_run(f"cd patches && git commit -a -m 'The patches for {version}.'")
|
||||
if s.returncode:
|
||||
die('Aborting')
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
|
||||
About to:
|
||||
- create signed tag for this release: {v_ver}
|
||||
- create release diffs, "{diff_name}"
|
||||
- create release tar, "{srctar_name}"
|
||||
- generate {rsync_ver}/patches/* files
|
||||
- create patches tar, "{pattar_name}"
|
||||
- update top-level README.md, NEWS.md, TODO, and ChangeLog
|
||||
- update top-level rsync*.html manpages
|
||||
- gpg-sign the release files
|
||||
- update hard-linked top-level release files{skipping}
|
||||
""")
|
||||
ans = input("<Press Enter to continue> ")
|
||||
|
||||
# TODO: is there a better way to ensure that our passphrase is in the agent?
|
||||
cmd_run("touch TeMp; gpg --sign TeMp; rm TeMp*")
|
||||
|
||||
out = cmd_txt(f"git tag -s -m 'Version {version}.' {v_ver}", capture='combined').out
|
||||
print(out, end='')
|
||||
if 'bad passphrase' in out or 'failed' in out:
|
||||
die('Aborting')
|
||||
|
||||
if os.path.isdir('patches/.git'):
|
||||
out = cmd_txt(f"cd patches && git tag -s -m 'Version {version}.' {v_ver}", capture='combined').out
|
||||
print(out, end='')
|
||||
if 'bad passphrase' in out or 'failed' in out:
|
||||
die('Aborting')
|
||||
|
||||
os.environ['PATH'] = ORIGINAL_PATH
|
||||
|
||||
# Extract the generated files from the old tar.
|
||||
tweaked_gen_files = [ os.path.join(rsync_lastver, fn) for fn in gen_files ]
|
||||
cmd_run(['tar', 'xzf', lasttar_file, *tweaked_gen_files])
|
||||
os.rename(rsync_lastver, 'a')
|
||||
|
||||
print(f"Creating {diff_file} ...")
|
||||
cmd_chk(['rsync', '-a', *gen_pathnames, 'b/'])
|
||||
|
||||
sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # CAUTION: must not contain any single quotes!
|
||||
cmd_chk(f"(git diff v{lastversion} {v_ver} -- ':!.github'; diff -upN a b | sed -r '{sed_script}') | gzip -9 >{diff_file}")
|
||||
shutil.rmtree('a')
|
||||
os.rename('b', rsync_ver)
|
||||
|
||||
print(f"Creating {srctar_file} ...")
|
||||
cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | tar xf -")
|
||||
cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver}/")
|
||||
cmd_chk(['fakeroot', 'tar', 'czf', srctar_file, '--exclude=.github', rsync_ver])
|
||||
shutil.rmtree(rsync_ver)
|
||||
|
||||
print(f'Updating files in "{rsync_ver}/patches" dir ...')
|
||||
os.mkdir(rsync_ver, 0o755)
|
||||
os.mkdir(f"{rsync_ver}/patches", 0o755)
|
||||
cmd_chk(f"packaging/patch-update --skip-check --branch={args.master_branch} --gen={rsync_ver}/patches".split())
|
||||
|
||||
print(f"Creating {pattar_file} ...")
|
||||
cmd_chk(['fakeroot', 'tar', 'chzf', pattar_file, rsync_ver + '/patches'])
|
||||
shutil.rmtree(rsync_ver)
|
||||
|
||||
print(f"Updating the other files in {dest} ...")
|
||||
md_files = 'README.md NEWS.md INSTALL.md'.split()
|
||||
html_files = [ fn for fn in gen_pathnames if fn.endswith('.html') ]
|
||||
cmd_chk(['rsync', '-a', *md_files, *html_files, dest])
|
||||
cmd_chk(["./md-convert", "--dest", dest, *md_files])
|
||||
|
||||
cmd_chk(f"git log --name-status | gzip -9 >{dest}/ChangeLog.gz")
|
||||
|
||||
for fn in (srctar_file, pattar_file, diff_file):
|
||||
asc_fn = fn + '.asc'
|
||||
if os.path.lexists(asc_fn):
|
||||
os.unlink(asc_fn)
|
||||
res = cmd_run(['gpg', '--batch', '-ba', fn])
|
||||
if res.returncode != 0 and res.returncode != 2:
|
||||
die("gpg signing failed")
|
||||
|
||||
if not pre:
|
||||
for find in f'{dest}/rsync-*.gz {dest}/rsync-*.asc {dest}/src-previews/rsync-*diffs.gz*'.split():
|
||||
for fn in glob.glob(find):
|
||||
os.unlink(fn)
|
||||
top_link = [
|
||||
srctar_file, f"{srctar_file}.asc",
|
||||
pattar_file, f"{pattar_file}.asc",
|
||||
diff_file, f"{diff_file}.asc",
|
||||
]
|
||||
for fn in top_link:
|
||||
os.link(fn, re.sub(r'/src(-\w+)?/', '/', fn))
|
||||
|
||||
print(f"""\
|
||||
{dash_line}
|
||||
|
||||
Local changes are done. When you're satisfied, push the git repository
|
||||
and rsync the release files. Remember to announce the release on *BOTH*
|
||||
rsync-announce@lists.samba.org and rsync@lists.samba.org (and the web)!
|
||||
""")
|
||||
|
||||
|
||||
def replace_or_die(regex, repl, txt, die_msg):
|
||||
m = regex.search(txt)
|
||||
if not m:
|
||||
die(die_msg)
|
||||
return regex.sub(repl, txt, 1)
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
die("\nAborting due to SIGINT.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Prepare a new release of rsync in the git repo & ftp dir.", add_help=False)
|
||||
parser.add_argument('--branch', '-b', dest='master_branch', default='master', help="The branch to release. Default: master.")
|
||||
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
705
packaging/release.py
Executable file
705
packaging/release.py
Executable file
@@ -0,0 +1,705 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Step-based release script for rsync. Each step is a separate invocation
|
||||
# selected by a --step-N-XX option, so the maintainer drives the release
|
||||
# manually one piece at a time.
|
||||
#
|
||||
# All persistent state and working files live in ../release/ (a sibling of
|
||||
# the rsync git checkout):
|
||||
#
|
||||
# ../release/rsync-ftp/ mirror of samba.org:/home/ftp/pub/rsync
|
||||
# ../release/rsync-html/ release-time snapshot of the html site
|
||||
# ../release/work/ scratch space for tarball / diff staging
|
||||
# ../release/release-state.json info shared between steps
|
||||
#
|
||||
# The rsync-patches archive is no longer maintained and has been dropped.
|
||||
#
|
||||
# Run "packaging/release.py --list" to see the step list.
|
||||
|
||||
import os, sys, re, argparse, glob, shutil, json, signal, subprocess
|
||||
from datetime import datetime
|
||||
|
||||
sys.path = ['packaging'] + sys.path
|
||||
|
||||
from pkglib import (
|
||||
warn, die, cmd_run, cmd_chk, cmd_txt, cmd_txt_chk, cmd_pipe,
|
||||
check_git_state, get_rsync_version,
|
||||
get_NEWS_version_info, get_protocol_versions,
|
||||
)
|
||||
|
||||
# ---------- Paths ----------
|
||||
|
||||
RELEASE_DIR = os.path.realpath('../release')
|
||||
FTP_DIR = os.path.join(RELEASE_DIR, 'rsync-ftp')
|
||||
HTML_DIR = os.path.join(RELEASE_DIR, 'rsync-html')
|
||||
WORK_DIR = os.path.join(RELEASE_DIR, 'work')
|
||||
STATE_FILE = os.path.join(RELEASE_DIR, 'release-state.json')
|
||||
|
||||
# The rsync-web/ subdirectory in the rsync source tree is the source-of-truth
|
||||
# for the git-tracked html content. step-1-fetch snapshots it into HTML_DIR
|
||||
# for the release flow, where it can be edited or augmented with server-side
|
||||
# content before step-11-push-html sends it to samba.org.
|
||||
HTML_SRC = os.path.realpath('rsync-web')
|
||||
|
||||
FTP_REMOTE_PATH = '/home/ftp/pub/rsync'
|
||||
HTML_REMOTE_PATH = '/home/httpd/html/rsync'
|
||||
|
||||
# Files that ./configure + make produce and that the release tarball / diff
|
||||
# need to bundle alongside the git-tracked source. Mirrors the GENFILES
|
||||
# definition in Makefile.in (with rrsync.1{,.html} since we always configure
|
||||
# --with-rrsync in --step-4-build).
|
||||
GEN_FILES = [
|
||||
'configure.sh',
|
||||
'aclocal.m4',
|
||||
'config.h.in',
|
||||
'rsync.1', 'rsync.1.html',
|
||||
'rsync-ssl.1', 'rsync-ssl.1.html',
|
||||
'rsyncd.conf.5', 'rsyncd.conf.5.html',
|
||||
'rrsync.1', 'rrsync.1.html',
|
||||
]
|
||||
|
||||
# ---------- Step registry ----------
|
||||
|
||||
STEPS = [
|
||||
('step-1-fetch', 'mirror ../release/rsync-ftp from samba.org and snapshot ../release/rsync-html from rsync-web/'),
|
||||
('step-2-prepare', 'gather release info interactively and write release-state.json'),
|
||||
('step-3-tweak', 'update version.h, rsync.h, NEWS.md, and packaging/*.spec'),
|
||||
('step-4-build', 'run smart-make + make gen'),
|
||||
('step-5-commit', 'git commit -a (commit the prepared release changes)'),
|
||||
('step-6-tag', 'create the gpg-signed git tag'),
|
||||
('step-7-tarball', 'build the source tarball and diffs.gz against the previous release'),
|
||||
('step-8-update-ftp', 'refresh README/NEWS/INSTALL/html in the ftp dir, regen ChangeLog.gz, gpg-sign tarballs'),
|
||||
('step-9-toplinks', 'hard-link top-level release files (final releases only)'),
|
||||
('step-10-push-ftp', 'rsync ../release/rsync-ftp/ to samba.org'),
|
||||
('step-11-push-html', 'rsync ../release/rsync-html/ to samba.org (after any manual edits)'),
|
||||
('step-12-push-git', 'print the git push commands for you to run'),
|
||||
]
|
||||
STEP_FLAGS = [s[0] for s in STEPS]
|
||||
|
||||
DASH_LINE = '=' * 74
|
||||
|
||||
# ---------- State helpers ----------
|
||||
|
||||
def load_state():
|
||||
if not os.path.isfile(STATE_FILE):
|
||||
die(f"{STATE_FILE} not found. Run --step-2-prepare first.")
|
||||
with open(STATE_FILE, 'r', encoding='utf-8') as fh:
|
||||
return json.load(fh)
|
||||
|
||||
|
||||
def save_state(state):
|
||||
os.makedirs(RELEASE_DIR, exist_ok=True)
|
||||
with open(STATE_FILE, 'w', encoding='utf-8') as fh:
|
||||
json.dump(state, fh, indent=2, sort_keys=True)
|
||||
fh.write('\n')
|
||||
|
||||
|
||||
def require_samba_host():
|
||||
host = os.environ.get('RSYNC_SAMBA_HOST', '')
|
||||
if not host.endswith('.samba.org'):
|
||||
die("Set RSYNC_SAMBA_HOST in your environment to the samba hostname (e.g. hr3.samba.org).")
|
||||
return host
|
||||
|
||||
|
||||
def require_top_of_checkout():
|
||||
if not os.path.isfile('packaging/release.py'):
|
||||
die("Run this script from the top of your rsync checkout.")
|
||||
if not os.path.isdir('.git'):
|
||||
die("There is no .git dir in the current directory.")
|
||||
|
||||
|
||||
def replace_or_die(regex, repl, txt, die_msg):
|
||||
m = regex.search(txt)
|
||||
if not m:
|
||||
die(die_msg)
|
||||
return regex.sub(repl, txt, 1)
|
||||
|
||||
|
||||
def section(title):
|
||||
print(f"\n{DASH_LINE}\n== {title}\n{DASH_LINE}")
|
||||
|
||||
|
||||
def confirm(prompt, default_no=True):
|
||||
suffix = '[n] ' if default_no else '[y] '
|
||||
ans = input(f"{prompt} {suffix}").strip().lower()
|
||||
if default_no:
|
||||
return ans.startswith('y')
|
||||
return ans == '' or ans.startswith('y')
|
||||
|
||||
|
||||
# ---------- Step 1: fetch ftp + html ----------
|
||||
|
||||
def step_1_fetch(args):
|
||||
host = require_samba_host()
|
||||
os.makedirs(RELEASE_DIR, exist_ok=True)
|
||||
os.makedirs(WORK_DIR, exist_ok=True)
|
||||
|
||||
section(f"Fetching ftp dir into {FTP_DIR}")
|
||||
if not os.path.isdir(FTP_DIR):
|
||||
os.makedirs(FTP_DIR)
|
||||
# packaging/ftp.filt is the authoritative copy of the .filt filter file
|
||||
# that controls which subtrees rsync excludes from the FTP mirror.
|
||||
# Seed FTP_DIR/.filt from it so the bundled version is what step-1's
|
||||
# rsync uses here, and so step-10-push-ftp propagates it back to the
|
||||
# server. --exclude=/.filt below stops the server's copy from
|
||||
# overwriting our bundled one on the way down.
|
||||
filt = os.path.join(FTP_DIR, '.filt')
|
||||
bundled_filt = os.path.realpath('packaging/ftp.filt')
|
||||
if not os.path.isfile(bundled_filt):
|
||||
die(f"{bundled_filt} not found; cannot seed .filt for the FTP pull.")
|
||||
shutil.copyfile(bundled_filt, filt)
|
||||
cmd_chk(['rsync', '-aivOHP', f'-f:_{filt}', '--exclude=/.filt',
|
||||
f'{host}:{FTP_REMOTE_PATH}/', f'{FTP_DIR}/'])
|
||||
|
||||
section(f"Snapshotting html dir from {HTML_SRC} into {HTML_DIR}")
|
||||
if not os.path.isdir(HTML_SRC):
|
||||
die(f"{HTML_SRC} not found. This should be the in-tree rsync-web/ "
|
||||
f"subdirectory; something is wrong with your checkout.")
|
||||
os.makedirs(HTML_DIR, exist_ok=True)
|
||||
cmd_chk(['rsync', '-aiv', f'{HTML_SRC}/', f'{HTML_DIR}/'])
|
||||
|
||||
# Then mirror non-git html content from the server, skipping files that
|
||||
# the html git already provides (driven by the 'filt' file in HTML_DIR).
|
||||
filt = os.path.join(HTML_DIR, 'filt')
|
||||
if os.path.exists(filt):
|
||||
tmp_filt = os.path.join(HTML_DIR, 'tmp-filt')
|
||||
cmd_chk(f"sed -n -e 's/[-P]/H/p' '{filt}' >'{tmp_filt}'")
|
||||
cmd_chk(['rsync', '-aivOHP', f'-f._{tmp_filt}',
|
||||
f'{host}:{HTML_REMOTE_PATH}/', f'{HTML_DIR}/'])
|
||||
os.unlink(tmp_filt)
|
||||
|
||||
print(f"\nFetch complete. Local dirs are now in {RELEASE_DIR}.")
|
||||
|
||||
|
||||
# ---------- Step 2: prepare ----------
|
||||
|
||||
def step_2_prepare(args):
|
||||
require_top_of_checkout()
|
||||
os.makedirs(RELEASE_DIR, exist_ok=True)
|
||||
|
||||
if not os.path.isdir(FTP_DIR):
|
||||
die(f"{FTP_DIR} does not exist. Run --step-1-fetch first.")
|
||||
|
||||
now = datetime.now().astimezone()
|
||||
cl_today = now.strftime('* %a %b %d %Y')
|
||||
year = now.strftime('%Y')
|
||||
ztoday = now.strftime('%d %b %Y')
|
||||
today = ztoday.lstrip('0')
|
||||
tz_now = now.strftime('%z')
|
||||
tz_num = tz_now[0:1].replace('+', '') + str(float(tz_now[1:3]) + float(tz_now[3:]) / 60)
|
||||
|
||||
curversion = get_rsync_version()
|
||||
lastversion, last_protocol_version, pdate = get_NEWS_version_info()
|
||||
protocol_version, subprotocol_version = get_protocol_versions()
|
||||
|
||||
# Default next version: bump preN, or move dev -> pre1.
|
||||
version = curversion
|
||||
m = re.search(r'pre(\d+)', version)
|
||||
if m:
|
||||
version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version)
|
||||
else:
|
||||
version = version.replace('dev', 'pre1')
|
||||
|
||||
print(f"\nCurrent version (version.h): {curversion}")
|
||||
print(f"Last released version (NEWS.md): {lastversion}")
|
||||
print(f"Current protocol version: {protocol_version} (last released: {last_protocol_version})")
|
||||
|
||||
ans = input(f"\nVersion to release [{version}, '.' to drop the preN suffix]: ").strip()
|
||||
if ans == '.':
|
||||
version = re.sub(r'pre\d+', '', version)
|
||||
elif ans:
|
||||
version = ans
|
||||
if not re.match(r'^[\d.]+(pre\d+)?$', version):
|
||||
die(f'Invalid version: "{version}"')
|
||||
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
|
||||
|
||||
if 'pre' in version and not curversion.endswith('dev'):
|
||||
lastversion = curversion
|
||||
|
||||
ans = input(f"Previous version to diff against [{lastversion}]: ").strip()
|
||||
if ans:
|
||||
lastversion = ans
|
||||
lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion)
|
||||
|
||||
m = re.search(r'(pre\d+)', version)
|
||||
pre = m[1] if m else ''
|
||||
finalversion = re.sub(r'pre\d+', '', version)
|
||||
|
||||
release = '0.1' if pre else '1'
|
||||
ans = input(f"RPM release number [{release}]: ").strip()
|
||||
if ans:
|
||||
release = ans
|
||||
if pre:
|
||||
release += '.' + pre
|
||||
|
||||
proto_changed = protocol_version != last_protocol_version
|
||||
if proto_changed:
|
||||
if finalversion in pdate:
|
||||
proto_change_date = pdate[finalversion]
|
||||
else:
|
||||
while True:
|
||||
ans = input(f"Date the protocol changed to {protocol_version} (dd Mmm yyyy): ").strip()
|
||||
if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans):
|
||||
break
|
||||
proto_change_date = ans
|
||||
else:
|
||||
proto_change_date = ' ' * 11
|
||||
|
||||
if 'pre' in lastversion:
|
||||
if not pre:
|
||||
die("Refusing to diff a release version against a pre-release version.")
|
||||
srcdir = srcdiffdir = lastsrcdir = 'src-previews'
|
||||
elif pre:
|
||||
srcdir = srcdiffdir = 'src-previews'
|
||||
lastsrcdir = 'src'
|
||||
else:
|
||||
srcdir = lastsrcdir = 'src'
|
||||
srcdiffdir = 'src-diffs'
|
||||
|
||||
state = {
|
||||
'version': version,
|
||||
'lastversion': lastversion,
|
||||
'finalversion': finalversion,
|
||||
'pre': pre,
|
||||
'release': release,
|
||||
'protocol_version': protocol_version,
|
||||
'subprotocol_version': subprotocol_version,
|
||||
'proto_changed': proto_changed,
|
||||
'proto_change_date': proto_change_date,
|
||||
'srcdir': srcdir,
|
||||
'srcdiffdir': srcdiffdir,
|
||||
'lastsrcdir': lastsrcdir,
|
||||
'today': today,
|
||||
'ztoday': ztoday,
|
||||
'cl_today': cl_today,
|
||||
'year': year,
|
||||
'tz_num': tz_num,
|
||||
'master_branch': args.master_branch,
|
||||
}
|
||||
save_state(state)
|
||||
|
||||
section("Release info")
|
||||
for k in ('version', 'lastversion', 'release', 'srcdir', 'srcdiffdir', 'lastsrcdir',
|
||||
'protocol_version', 'proto_changed', 'proto_change_date'):
|
||||
print(f" {k}: {state[k]}")
|
||||
print(f"\nWrote {STATE_FILE}. Re-run --step-2-prepare to change anything.")
|
||||
|
||||
|
||||
# ---------- Step 3: tweak version files ----------
|
||||
|
||||
def step_3_tweak(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
version = state['version']
|
||||
finalversion = state['finalversion']
|
||||
pre = state['pre']
|
||||
release = state['release']
|
||||
today = state['today']
|
||||
ztoday = state['ztoday']
|
||||
cl_today = state['cl_today']
|
||||
year = state['year']
|
||||
tz_num = state['tz_num']
|
||||
proto_changed = state['proto_changed']
|
||||
proto_change_date = state['proto_change_date']
|
||||
protocol_version = state['protocol_version']
|
||||
srcdir = state['srcdir']
|
||||
|
||||
specvars = {
|
||||
'Version:': finalversion,
|
||||
'Release:': release,
|
||||
'%define fullversion': f'%{{version}}{pre}',
|
||||
'Released': version + '.',
|
||||
'%define srcdir': srcdir,
|
||||
}
|
||||
|
||||
tweak_files = ['version.h', 'rsync.h', 'NEWS.md']
|
||||
tweak_files += glob.glob('packaging/*.spec')
|
||||
tweak_files += glob.glob('packaging/*/*.spec')
|
||||
|
||||
for fn in tweak_files:
|
||||
with open(fn, 'r', encoding='utf-8') as fh:
|
||||
old_txt = txt = fh.read()
|
||||
if fn == 'version.h':
|
||||
x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M)
|
||||
txt = replace_or_die(x_re, r'\1 "%s"' % version, txt,
|
||||
f"Unable to update RSYNC_VERSION in {fn}")
|
||||
x_re = re.compile(r'^(#define MAINTAINER_TZ_OFFSET).*', re.M)
|
||||
txt = replace_or_die(x_re, r'\1 ' + tz_num, txt,
|
||||
f"Unable to update MAINTAINER_TZ_OFFSET in {fn}")
|
||||
elif fn == 'rsync.h':
|
||||
x_re = re.compile(r'(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
|
||||
repl = lambda m: m[1] + ' ' + (
|
||||
'0' if not pre or not proto_changed
|
||||
else '1' if m[2] == '0'
|
||||
else m[2])
|
||||
txt = replace_or_die(x_re, repl, txt,
|
||||
f"Unable to find SUBPROTOCOL_VERSION in {fn}")
|
||||
elif fn == 'NEWS.md':
|
||||
efv = re.escape(finalversion)
|
||||
x_re = re.compile(
|
||||
r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
|
||||
+ r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?')
|
||||
rel_day = 'UNRELEASED' if pre else today
|
||||
repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n'
|
||||
+ '## Changes in this version:\n')
|
||||
if proto_changed:
|
||||
repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n'
|
||||
good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1)
|
||||
msg = (f"The top of {fn} is not in the right format. It should be:\n" + good_top)
|
||||
txt = replace_or_die(x_re, repl, txt, msg)
|
||||
x_re = re.compile(
|
||||
r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv,
|
||||
re.M)
|
||||
repl = lambda m: (m[1] + (m[2] if pre else ztoday) + m[3]
|
||||
+ proto_change_date + m[4] + protocol_version + m[5])
|
||||
txt = replace_or_die(x_re, repl, txt,
|
||||
f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}')
|
||||
elif '.spec' in fn:
|
||||
for var, val in specvars.items():
|
||||
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
|
||||
txt = replace_or_die(x_re, var + ' ' + val, txt,
|
||||
f"Unable to update {var} in {fn}")
|
||||
x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
|
||||
txt = replace_or_die(x_re, r'%s \1' % cl_today, txt,
|
||||
f"Unable to update ChangeLog header in {fn}")
|
||||
else:
|
||||
die(f"Unrecognized file in tweak_files: {fn}")
|
||||
|
||||
if txt != old_txt:
|
||||
print(f"Updating {fn}")
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(txt)
|
||||
|
||||
cmd_chk(['packaging/year-tweak'])
|
||||
|
||||
section("git diff after tweaks")
|
||||
cmd_run(['git', '--no-pager', 'diff'])
|
||||
|
||||
|
||||
# ---------- Step 4: build ----------
|
||||
|
||||
def step_4_build(args):
|
||||
require_top_of_checkout()
|
||||
load_state() # just to ensure we've prepared
|
||||
|
||||
section("Running prepare-source + configure --prefix=/usr --with-rrsync + make + make gen")
|
||||
# Always re-prepare so configure.sh is current; we run configure ourselves
|
||||
# with the release-required flags rather than relying on the cached
|
||||
# config.status (which may have been produced with different options).
|
||||
if os.path.isfile('.fetch'):
|
||||
cmd_chk(['./prepare-source', 'fetch'])
|
||||
else:
|
||||
cmd_chk(['./prepare-source'])
|
||||
|
||||
cmd_chk(['./configure', '--prefix=/usr', '--with-rrsync'])
|
||||
cmd_chk(['make'])
|
||||
cmd_chk(['make', 'gen'])
|
||||
|
||||
|
||||
# ---------- Step 5: commit ----------
|
||||
|
||||
def step_5_commit(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
version = state['version']
|
||||
|
||||
section("git status")
|
||||
cmd_run(['git', 'status'])
|
||||
if not confirm("Commit all current changes with the release message?"):
|
||||
die("Aborted.")
|
||||
cmd_chk(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]'])
|
||||
|
||||
|
||||
# ---------- Step 6: tag ----------
|
||||
|
||||
def step_6_tag(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
version = state['version']
|
||||
v_ver = 'v' + version
|
||||
|
||||
out = cmd_txt_chk(['git', 'tag', '-l', v_ver]).out
|
||||
if out.strip():
|
||||
if not confirm(f"Tag {v_ver} already exists. Delete and recreate?"):
|
||||
die("Aborted.")
|
||||
cmd_chk(['git', 'tag', '-d', v_ver])
|
||||
|
||||
# Prime the gpg agent so the actual tag signing won't prompt.
|
||||
section("Priming gpg agent")
|
||||
cmd_run("touch TeMp; gpg --sign TeMp; rm -f TeMp TeMp.gpg")
|
||||
|
||||
section(f"Creating signed tag {v_ver}")
|
||||
out = cmd_txt(['git', 'tag', '-s', '-m', f'Version {version}.', v_ver],
|
||||
capture='combined').out
|
||||
print(out, end='')
|
||||
if 'bad passphrase' in out.lower() or 'failed' in out.lower():
|
||||
die("Tag creation failed.")
|
||||
|
||||
|
||||
# ---------- Step 7: tarball + diff ----------
|
||||
|
||||
def step_7_tarball(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
version = state['version']
|
||||
lastversion = state['lastversion']
|
||||
pre = state['pre']
|
||||
srcdir = state['srcdir']
|
||||
srcdiffdir = state['srcdiffdir']
|
||||
lastsrcdir = state['lastsrcdir']
|
||||
|
||||
rsync_ver = 'rsync-' + version
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
v_ver = 'v' + version
|
||||
|
||||
srctar_name = f"{rsync_ver}.tar.gz"
|
||||
diff_name = f"{rsync_lastver}-{version}.diffs.gz"
|
||||
|
||||
srctar_file = os.path.join(FTP_DIR, srcdir, srctar_name)
|
||||
diff_file = os.path.join(FTP_DIR, srcdiffdir, diff_name)
|
||||
lasttar_file = os.path.join(FTP_DIR, lastsrcdir, rsync_lastver + '.tar.gz')
|
||||
|
||||
for d in (os.path.dirname(srctar_file), os.path.dirname(diff_file)):
|
||||
os.makedirs(d, exist_ok=True)
|
||||
if not os.path.isfile(lasttar_file):
|
||||
die(f"Previous tarball not found: {lasttar_file}")
|
||||
|
||||
# Stage in ../release/work to keep the source checkout clean.
|
||||
if os.path.isdir(WORK_DIR):
|
||||
shutil.rmtree(WORK_DIR)
|
||||
os.makedirs(WORK_DIR)
|
||||
|
||||
a_dir = os.path.join(WORK_DIR, 'a')
|
||||
b_dir = os.path.join(WORK_DIR, 'b')
|
||||
|
||||
# Extract gen files from the previous tarball into work/a/.
|
||||
tweaked_gen_files = [os.path.join(rsync_lastver, fn) for fn in GEN_FILES]
|
||||
cmd_chk(['tar', '-C', WORK_DIR, '-xzf', lasttar_file, *tweaked_gen_files])
|
||||
os.rename(os.path.join(WORK_DIR, rsync_lastver), a_dir)
|
||||
|
||||
# Copy current gen files (built in the top-level checkout) into work/b/.
|
||||
os.makedirs(b_dir)
|
||||
cmd_chk(['rsync', '-a', *GEN_FILES, b_dir + '/'])
|
||||
|
||||
section(f"Creating {diff_file}")
|
||||
sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # no single quotes!
|
||||
cmd_chk(
|
||||
f"(git diff v{lastversion} {v_ver} -- ':!.github'; "
|
||||
f"diff -upN {a_dir} {b_dir} | sed -r '{sed_script}') | gzip -9 >{diff_file}")
|
||||
|
||||
section(f"Creating {srctar_file}")
|
||||
# Reuse work/b/ (which already holds the fresh gen files) as the release
|
||||
# staging dir, then let "git archive" overlay the git-tracked source files
|
||||
# on top. That way the tarball ends up with both gen files and source.
|
||||
rsync_ver_dir = os.path.join(WORK_DIR, rsync_ver)
|
||||
shutil.rmtree(a_dir)
|
||||
os.rename(b_dir, rsync_ver_dir)
|
||||
cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | "
|
||||
f"tar -C {WORK_DIR} -xf -")
|
||||
cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver_dir}/")
|
||||
cmd_chk(['fakeroot', 'tar', '-C', WORK_DIR, '-czf', srctar_file,
|
||||
'--exclude=.github', rsync_ver])
|
||||
|
||||
# Leave staging in place; --step-8-update-ftp does its own thing.
|
||||
print(f"\nCreated:\n {srctar_file}\n {diff_file}")
|
||||
|
||||
|
||||
# ---------- Step 8: update ftp ----------
|
||||
|
||||
def step_8_update_ftp(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
version = state['version']
|
||||
lastversion = state['lastversion']
|
||||
srcdir = state['srcdir']
|
||||
srcdiffdir = state['srcdiffdir']
|
||||
|
||||
rsync_ver = 'rsync-' + version
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
srctar_file = os.path.join(FTP_DIR, srcdir, f"{rsync_ver}.tar.gz")
|
||||
diff_file = os.path.join(FTP_DIR, srcdiffdir,
|
||||
f"{rsync_lastver}-{version}.diffs.gz")
|
||||
|
||||
section(f"Refreshing top-of-tree files in {FTP_DIR}")
|
||||
md_files = ['README.md', 'NEWS.md', 'INSTALL.md']
|
||||
html_files = [fn for fn in GEN_FILES if fn.endswith('.html')]
|
||||
cmd_chk(['rsync', '-a', *md_files, *html_files, FTP_DIR + '/'])
|
||||
cmd_chk(['./md-convert', '--dest', FTP_DIR, *md_files])
|
||||
|
||||
section(f"Regenerating {FTP_DIR}/ChangeLog.gz")
|
||||
cmd_chk(f"git log --name-status | gzip -9 >{FTP_DIR}/ChangeLog.gz")
|
||||
|
||||
# Prime gpg agent and then sign the tar + diff.
|
||||
section("Priming gpg agent")
|
||||
cmd_run("touch TeMp; gpg --sign TeMp; rm -f TeMp TeMp.gpg")
|
||||
|
||||
for fn in (srctar_file, diff_file):
|
||||
if not os.path.isfile(fn):
|
||||
die(f"Missing file to sign: {fn}. Did --step-7-tarball run successfully?")
|
||||
asc_fn = fn + '.asc'
|
||||
if os.path.lexists(asc_fn):
|
||||
os.unlink(asc_fn)
|
||||
section(f"GPG-signing {fn}")
|
||||
res = cmd_run(['gpg', '--batch', '-ba', fn])
|
||||
if res.returncode not in (0, 2):
|
||||
die("gpg signing failed.")
|
||||
|
||||
|
||||
# ---------- Step 9: top-level hard links ----------
|
||||
|
||||
def step_9_toplinks(args):
|
||||
require_top_of_checkout()
|
||||
state = load_state()
|
||||
|
||||
pre = state['pre']
|
||||
if pre:
|
||||
print("Skipping: pre-releases do not get top-level hard links.")
|
||||
return
|
||||
|
||||
version = state['version']
|
||||
lastversion = state['lastversion']
|
||||
srcdir = state['srcdir']
|
||||
srcdiffdir = state['srcdiffdir']
|
||||
|
||||
rsync_ver = 'rsync-' + version
|
||||
rsync_lastver = 'rsync-' + lastversion
|
||||
srctar_file = os.path.join(FTP_DIR, srcdir, f"{rsync_ver}.tar.gz")
|
||||
diff_file = os.path.join(FTP_DIR, srcdiffdir,
|
||||
f"{rsync_lastver}-{version}.diffs.gz")
|
||||
|
||||
section("Removing stale top-level rsync-* files")
|
||||
for find in [f'{FTP_DIR}/rsync-*.gz',
|
||||
f'{FTP_DIR}/rsync-*.asc',
|
||||
f'{FTP_DIR}/src-previews/rsync-*diffs.gz*']:
|
||||
for fn in glob.glob(find):
|
||||
os.unlink(fn)
|
||||
|
||||
top_link = [
|
||||
srctar_file, srctar_file + '.asc',
|
||||
diff_file, diff_file + '.asc',
|
||||
]
|
||||
for fn in top_link:
|
||||
target = re.sub(r'/src(-\w+)?/', '/', fn)
|
||||
if os.path.lexists(target):
|
||||
os.unlink(target)
|
||||
os.link(fn, target)
|
||||
print(f" linked {target}")
|
||||
|
||||
|
||||
# ---------- Step 10: push ftp ----------
|
||||
|
||||
def step_10_push_ftp(args):
|
||||
host = require_samba_host()
|
||||
if not os.path.isdir(FTP_DIR):
|
||||
die(f"{FTP_DIR} does not exist. Run --step-1-fetch first.")
|
||||
section(f"rsync ftp dir to {host}")
|
||||
rsync_with_confirm(['-aivOHP', '--chown=:rsync', '--del',
|
||||
f'-f._{os.path.join(FTP_DIR, ".filt")}',
|
||||
f'{FTP_DIR}/', f'{host}:{FTP_REMOTE_PATH}/'])
|
||||
|
||||
|
||||
# ---------- Step 11: push html ----------
|
||||
|
||||
def step_11_push_html(args):
|
||||
host = require_samba_host()
|
||||
if not os.path.isdir(HTML_DIR):
|
||||
die(f"{HTML_DIR} does not exist. Run --step-1-fetch first.")
|
||||
section(f"rsync html dir to {host}")
|
||||
filt = os.path.join(HTML_DIR, 'filt')
|
||||
rsync_with_confirm(['-aivOHP', '--chown=:rsync', '--del',
|
||||
f'-f._{filt}',
|
||||
f'{HTML_DIR}/', f'{host}:{HTML_REMOTE_PATH}/'])
|
||||
|
||||
|
||||
# ---------- Step 12: print push-git instructions ----------
|
||||
|
||||
def step_12_push_git(args):
|
||||
state = load_state()
|
||||
version = state['version']
|
||||
master_branch = state['master_branch']
|
||||
v_ver = 'v' + version
|
||||
|
||||
print(f"""\
|
||||
{DASH_LINE}
|
||||
Run these from the rsync-git checkout (this script does not push for you):
|
||||
|
||||
git push origin {master_branch}
|
||||
git push origin {v_ver}
|
||||
|
||||
If you have a 'samba' remote configured (git.samba.org:/data/git/rsync.git):
|
||||
|
||||
git push samba {master_branch}
|
||||
git push samba {v_ver}
|
||||
|
||||
Then upload the tarball + .asc to the GitHub release for {v_ver},
|
||||
and announce on rsync-announce@, rsync@, and Discord.
|
||||
""")
|
||||
|
||||
|
||||
# ---------- shared rsync-with-confirm ----------
|
||||
|
||||
def rsync_with_confirm(rsync_args):
|
||||
"""Run an rsync command in dry-run mode, then ask before running for real."""
|
||||
cmd_run(['rsync', '--dry-run', *rsync_args])
|
||||
if confirm("Run without --dry-run?"):
|
||||
cmd_run(['rsync', *rsync_args])
|
||||
|
||||
|
||||
# ---------- dispatch ----------
|
||||
|
||||
STEP_FUNCS = {
|
||||
'step-1-fetch': step_1_fetch,
|
||||
'step-2-prepare': step_2_prepare,
|
||||
'step-3-tweak': step_3_tweak,
|
||||
'step-4-build': step_4_build,
|
||||
'step-5-commit': step_5_commit,
|
||||
'step-6-tag': step_6_tag,
|
||||
'step-7-tarball': step_7_tarball,
|
||||
'step-8-update-ftp': step_8_update_ftp,
|
||||
'step-9-toplinks': step_9_toplinks,
|
||||
'step-10-push-ftp': step_10_push_ftp,
|
||||
'step-11-push-html': step_11_push_html,
|
||||
'step-12-push-git': step_12_push_git,
|
||||
}
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
die("\nAborting due to SIGINT.")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Step-based release script for rsync.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="Run --list to see the steps. Each invocation runs exactly one --step-* option.")
|
||||
parser.add_argument('--branch', '-b', dest='master_branch', default='master',
|
||||
help="The branch to release (default: master).")
|
||||
parser.add_argument('--list', action='store_true',
|
||||
help="List all release steps and exit.")
|
||||
grp = parser.add_mutually_exclusive_group()
|
||||
for flag, descr in STEPS:
|
||||
grp.add_argument('--' + flag, dest='step', action='store_const',
|
||||
const=flag, help=descr)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list:
|
||||
print("Release steps:")
|
||||
for flag, descr in STEPS:
|
||||
print(f" --{flag:18s} {descr}")
|
||||
return
|
||||
|
||||
if not args.step:
|
||||
parser.error("pick one --step-N-XX option (or --list to see them).")
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
os.environ['LESS'] = 'mqeiXR'
|
||||
STEP_FUNCS[args.step](args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
# vim: sw=4 et ft=python
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
import os, sys, re, argparse, glob
|
||||
|
||||
VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z]\S*\s+.*);', re.M)
|
||||
VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z][^ \n\t:]*\s+.*);', re.M)
|
||||
EXTERNS_RE = re.compile(r'^extern\s+(.*);', re.M)
|
||||
|
||||
types = { }
|
||||
sizes = { }
|
||||
|
||||
def main():
|
||||
@@ -68,19 +69,44 @@ def parse_vars(fn, lines):
|
||||
for line in lines:
|
||||
line = re.sub(r'\s*\{.*\}', '', line)
|
||||
line = re.sub(r'\s*\(.*\)', '', line)
|
||||
for item in re.split(r'\s*,\s*', line):
|
||||
item = re.sub(r'\s*=.*', '', item)
|
||||
m = re.search(r'(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
|
||||
line = re.sub(r'\s*=\s*[^,]*', '', line)
|
||||
m = re.search(r'^(?:(?:static|extern)\s+)?(?P<type>[^\[,]+?)(?P<vars>\w+([\[,].+)?)$', line)
|
||||
if not m:
|
||||
print(f"Bogus match? ({line})")
|
||||
continue
|
||||
items = m['vars']
|
||||
main_type = m['type'].strip()
|
||||
mt_len = len(main_type)
|
||||
main_type = main_type.rstrip('*')
|
||||
first_stars = '*' * (mt_len - len(main_type))
|
||||
if first_stars:
|
||||
main_type = main_type.rstrip()
|
||||
items = first_stars + items
|
||||
for item in re.split(r'\s*,\s*', items):
|
||||
m = re.search(r'(?P<stars>\*+\s*)?(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
|
||||
if not m:
|
||||
print(f"Bogus match? ({item})")
|
||||
continue
|
||||
if m['sz']:
|
||||
if m['var'] in sizes:
|
||||
if sizes[m['var']] != m['sz']:
|
||||
typ = main_type
|
||||
if m['stars']:
|
||||
typ = typ + m['stars'].strip()
|
||||
chk = [
|
||||
'type', typ, types,
|
||||
'size', m['sz'], sizes,
|
||||
]
|
||||
while chk:
|
||||
label = chk.pop(0)
|
||||
new = chk.pop(0)
|
||||
lst = chk.pop(0)
|
||||
if label == 'type':
|
||||
new = ' '.join(new.split()).replace(' *', '*')
|
||||
if m['var'] in lst:
|
||||
old = lst[m['var']]
|
||||
if new != old:
|
||||
var = m['var']
|
||||
print(fn, f'has inconsistent size for "{var}":', m['sz'], 'vs', sizes[var])
|
||||
print(fn, f'has inconsistent {label} for "{var}":', new, 'vs', old)
|
||||
else:
|
||||
sizes[m['var']] = m['sz']
|
||||
lst[m['var']] = new
|
||||
ret.append(m['var'])
|
||||
return ret
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
import sys, os, re, argparse, subprocess
|
||||
from datetime import datetime
|
||||
|
||||
MAINTAINER_NAME = 'Wayne Davison'
|
||||
MAINTAINER_SUF = ' ' + MAINTAINER_NAME + "\n"
|
||||
|
||||
def main():
|
||||
latest_year = '2000'
|
||||
|
||||
@@ -22,10 +19,6 @@ def main():
|
||||
m = argparse.Namespace(**m.groupdict())
|
||||
if m.year > latest_year:
|
||||
latest_year = m.year
|
||||
if m.fn.startswith('zlib/') or m.fn.startswith('popt/'):
|
||||
continue
|
||||
if re.search(r'\.(c|h|sh|test)$', m.fn):
|
||||
maybe_edit_copyright_year(m.fn, m.year)
|
||||
proc.communicate()
|
||||
|
||||
fn = 'latest-year.h'
|
||||
@@ -39,55 +32,8 @@ def main():
|
||||
fh.write(txt)
|
||||
|
||||
|
||||
def maybe_edit_copyright_year(fn, year):
|
||||
opening_lines = [ ]
|
||||
copyright_line = None
|
||||
|
||||
with open(fn, 'r', encoding='utf-8') as fh:
|
||||
for lineno, line in enumerate(fh):
|
||||
opening_lines.append(line)
|
||||
if lineno > 3 and not re.search(r'\S', line):
|
||||
break
|
||||
m = re.match(r'^(?P<pre>.*Copyright\s+\S+\s+)(?P<year>\d\d\d\d(?:-\d\d\d\d)?(,\s+\d\d\d\d)*)(?P<suf>.+)', line)
|
||||
if not m:
|
||||
continue
|
||||
copyright_line = argparse.Namespace(**m.groupdict())
|
||||
copyright_line.lineno = len(opening_lines)
|
||||
copyright_line.is_maintainer_line = MAINTAINER_NAME in copyright_line.suf
|
||||
copyright_line.txt = line
|
||||
if copyright_line.is_maintainer_line:
|
||||
break
|
||||
|
||||
if not copyright_line:
|
||||
return
|
||||
|
||||
if copyright_line.is_maintainer_line:
|
||||
cyears = copyright_line.year.split('-')
|
||||
if year == cyears[0]:
|
||||
cyears = [ year ]
|
||||
else:
|
||||
cyears = [ cyears[0], year ]
|
||||
txt = copyright_line.pre + '-'.join(cyears) + MAINTAINER_SUF
|
||||
if txt == copyright_line.txt:
|
||||
return
|
||||
opening_lines[copyright_line.lineno - 1] = txt
|
||||
else:
|
||||
if fn.startswith('lib/') or fn.startswith('testsuite/'):
|
||||
return
|
||||
txt = copyright_line.pre + year + MAINTAINER_SUF
|
||||
opening_lines[copyright_line.lineno - 1] += txt
|
||||
|
||||
remaining_txt = fh.read()
|
||||
|
||||
print(f"Updating {fn} with year {year}")
|
||||
|
||||
with open(fn, 'w', encoding='utf-8') as fh:
|
||||
fh.write(''.join(opening_lines))
|
||||
fh.write(remaining_txt)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Grab the year of last mod for our c & h files and make sure the Copyright comment is up-to-date.")
|
||||
parser = argparse.ArgumentParser(description="Grab the year of the last mod for our c & h files and make sure the LATEST_YEAR value is accurate.")
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/findme.c
|
||||
*/
|
||||
|
||||
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
file accompanying popt source distributions, available from
|
||||
ftp://ftp.rpm.org/pub/rpm/dist. */
|
||||
|
||||
#include "system.h"
|
||||
#include "findme.h"
|
||||
|
||||
const char * findProgramPath(const char * argv0)
|
||||
{
|
||||
char * path = getenv("PATH");
|
||||
char * pathbuf;
|
||||
char * start, * chptr;
|
||||
char * buf;
|
||||
size_t bufsize;
|
||||
|
||||
if (argv0 == NULL) return NULL; /* XXX can't happen */
|
||||
/* If there is a / in the argv[0], it has to be an absolute path */
|
||||
if (strchr(argv0, '/'))
|
||||
return xstrdup(argv0);
|
||||
|
||||
if (path == NULL) return NULL;
|
||||
|
||||
bufsize = strlen(path) + 1;
|
||||
start = pathbuf = alloca(bufsize);
|
||||
if (pathbuf == NULL) return NULL; /* XXX can't happen */
|
||||
strlcpy(pathbuf, path, bufsize);
|
||||
bufsize += sizeof "/" - 1 + strlen(argv0);
|
||||
buf = malloc(bufsize);
|
||||
if (buf == NULL) return NULL; /* XXX can't happen */
|
||||
|
||||
chptr = NULL;
|
||||
/*@-branchstate@*/
|
||||
do {
|
||||
if ((chptr = strchr(start, ':')))
|
||||
*chptr = '\0';
|
||||
snprintf(buf, bufsize, "%s/%s", start, argv0);
|
||||
|
||||
if (!access(buf, X_OK))
|
||||
return buf;
|
||||
|
||||
if (chptr)
|
||||
start = chptr + 1;
|
||||
else
|
||||
start = NULL;
|
||||
} while (start && *start);
|
||||
/*@=branchstate@*/
|
||||
|
||||
free(buf);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/findme.h
|
||||
*/
|
||||
|
||||
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
file accompanying popt source distributions, available from
|
||||
ftp://ftp.rpm.org/pub/rpm/dist. */
|
||||
|
||||
#ifndef H_FINDME
|
||||
#define H_FINDME
|
||||
|
||||
/**
|
||||
* Return absolute path to executable by searching PATH.
|
||||
* @param argv0 name of executable
|
||||
* @return (malloc'd) absolute path to executable (or NULL)
|
||||
*/
|
||||
/*@null@*/ const char * findProgramPath(/*@null@*/ const char * argv0)
|
||||
/*@*/;
|
||||
|
||||
#endif
|
||||
959
popt/lookup3.c
Normal file
959
popt/lookup3.c
Normal file
@@ -0,0 +1,959 @@
|
||||
/* -------------------------------------------------------------------- */
|
||||
/*
|
||||
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
*
|
||||
* These are functions for producing 32-bit hashes for hash table lookup.
|
||||
* jlu32w(), jlu32l(), jlu32lpair(), jlu32b(), _JLU3_MIX(), and _JLU3_FINAL()
|
||||
* are externally useful functions. Routines to test the hash are included
|
||||
* if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
* the public domain. It has no warranty.
|
||||
*
|
||||
* You probably want to use jlu32l(). jlu32l() and jlu32b()
|
||||
* hash byte arrays. jlu32l() is is faster than jlu32b() on
|
||||
* little-endian machines. Intel and AMD are little-endian machines.
|
||||
* On second thought, you probably want jlu32lpair(), which is identical to
|
||||
* jlu32l() except it returns two 32-bit hashes for the price of one.
|
||||
* You could implement jlu32bpair() if you wanted but I haven't bothered here.
|
||||
*
|
||||
* If you want to find a hash of, say, exactly 7 integers, do
|
||||
* a = i1; b = i2; c = i3;
|
||||
* _JLU3_MIX(a,b,c);
|
||||
* a += i4; b += i5; c += i6;
|
||||
* _JLU3_MIX(a,b,c);
|
||||
* a += i7;
|
||||
* _JLU3_FINAL(a,b,c);
|
||||
* then use c as the hash value. If you have a variable size array of
|
||||
* 4-byte integers to hash, use jlu32w(). If you have a byte array (like
|
||||
* a character string), use jlu32l(). If you have several byte arrays, or
|
||||
* a mix of things, see the comments above jlu32l().
|
||||
*
|
||||
* Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||
* then mix those integers. This is fast (you can do a lot more thorough
|
||||
* mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||
* on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_JLU3_SELFTEST)
|
||||
# define _JLU3_jlu32w 1
|
||||
# define _JLU3_jlu32l 1
|
||||
# define _JLU3_jlu32lpair 1
|
||||
# define _JLU3_jlu32b 1
|
||||
#endif
|
||||
|
||||
static const union _dbswap {
|
||||
const uint32_t ui;
|
||||
const unsigned char uc[4];
|
||||
} endian = { .ui = 0x11223344 };
|
||||
# define HASH_LITTLE_ENDIAN (endian.uc[0] == (unsigned char) 0x44)
|
||||
# define HASH_BIG_ENDIAN (endian.uc[0] == (unsigned char) 0x11)
|
||||
|
||||
#ifndef ROTL32
|
||||
# define ROTL32(x, s) (((x) << (s)) | ((x) >> (32 - (s))))
|
||||
#endif
|
||||
|
||||
/* NOTE: The _size parameter should be in bytes. */
|
||||
#define _JLU3_INIT(_h, _size) (0xdeadbeef + ((uint32_t)(_size)) + (_h))
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/*
|
||||
* _JLU3_MIX -- mix 3 32-bit values reversibly.
|
||||
*
|
||||
* This is reversible, so any information in (a,b,c) before _JLU3_MIX() is
|
||||
* still in (a,b,c) after _JLU3_MIX().
|
||||
*
|
||||
* If four pairs of (a,b,c) inputs are run through _JLU3_MIX(), or through
|
||||
* _JLU3_MIX() in reverse, there are at least 32 bits of the output that
|
||||
* are sometimes the same for one pair and different for another pair.
|
||||
* This was tested for:
|
||||
* * pairs that differed by one bit, by two bits, in any combination
|
||||
* of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
* (a,b,c).
|
||||
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
* the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
* is commonly produced by subtraction) look like a single 1-bit
|
||||
* difference.
|
||||
* * the base values were pseudorandom, all zero but one bit set, or
|
||||
* all zero plus a counter that starts at zero.
|
||||
*
|
||||
* Some k values for my "a-=c; a^=ROTL32(c,k); c+=b;" arrangement that
|
||||
* satisfy this are
|
||||
* 4 6 8 16 19 4
|
||||
* 9 15 3 18 27 15
|
||||
* 14 9 3 7 17 3
|
||||
* Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||
* for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||
* used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||
* the operations, constants, and arrangements of the variables.
|
||||
*
|
||||
* This does not achieve avalanche. There are input bits of (a,b,c)
|
||||
* that fail to affect some output bits of (a,b,c), especially of a. The
|
||||
* most thoroughly mixed value is c, but it doesn't really even achieve
|
||||
* avalanche in c.
|
||||
*
|
||||
* This allows some parallelism. Read-after-writes are good at doubling
|
||||
* the number of bits affected, so the goal of mixing pulls in the opposite
|
||||
* direction as the goal of parallelism. I did what I could. Rotates
|
||||
* seem to cost as much as shifts on every machine I could lay my hands
|
||||
* on, and rotates are much kinder to the top and bottom bits, so I used
|
||||
* rotates.
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
#define _JLU3_MIX(a,b,c) \
|
||||
{ \
|
||||
a -= c; a ^= ROTL32(c, 4); c += b; \
|
||||
b -= a; b ^= ROTL32(a, 6); a += c; \
|
||||
c -= b; c ^= ROTL32(b, 8); b += a; \
|
||||
a -= c; a ^= ROTL32(c,16); c += b; \
|
||||
b -= a; b ^= ROTL32(a,19); a += c; \
|
||||
c -= b; c ^= ROTL32(b, 4); b += a; \
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* _JLU3_FINAL -- final mixing of 3 32-bit values (a,b,c) into c
|
||||
*
|
||||
* Pairs of (a,b,c) values differing in only a few bits will usually
|
||||
* produce values of c that look totally different. This was tested for
|
||||
* * pairs that differed by one bit, by two bits, in any combination
|
||||
* of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
* (a,b,c).
|
||||
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
* the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
* is commonly produced by subtraction) look like a single 1-bit
|
||||
* difference.
|
||||
* * the base values were pseudorandom, all zero but one bit set, or
|
||||
* all zero plus a counter that starts at zero.
|
||||
*
|
||||
* These constants passed:
|
||||
* 14 11 25 16 4 14 24
|
||||
* 12 14 25 16 4 14 24
|
||||
* and these came close:
|
||||
* 4 8 15 26 3 22 24
|
||||
* 10 8 15 26 3 22 24
|
||||
* 11 8 15 26 3 22 24
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
#define _JLU3_FINAL(a,b,c) \
|
||||
{ \
|
||||
c ^= b; c -= ROTL32(b,14); \
|
||||
a ^= c; a -= ROTL32(c,11); \
|
||||
b ^= a; b -= ROTL32(a,25); \
|
||||
c ^= b; c -= ROTL32(b,16); \
|
||||
a ^= c; a -= ROTL32(c,4); \
|
||||
b ^= a; b -= ROTL32(a,14); \
|
||||
c ^= b; c -= ROTL32(b,24); \
|
||||
}
|
||||
|
||||
#if defined(_JLU3_jlu32w)
|
||||
uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size);
|
||||
/* -------------------------------------------------------------------- */
|
||||
/**
|
||||
* This works on all machines. To be useful, it requires
|
||||
* -- that the key be an array of uint32_t's, and
|
||||
* -- that the size be the number of uint32_t's in the key
|
||||
*
|
||||
* The function jlu32w() is identical to jlu32l() on little-endian
|
||||
* machines, and identical to jlu32b() on big-endian machines,
|
||||
* except that the size has to be measured in uint32_ts rather than in
|
||||
* bytes. jlu32l() is more complicated than jlu32w() only because
|
||||
* jlu32l() has to dance around fitting the key bytes into registers.
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *k the key, an array of uint32_t values
|
||||
* @param size the size of the key, in uint32_ts
|
||||
* @return the lookup3 hash
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size)
|
||||
{
|
||||
uint32_t a = _JLU3_INIT(h, (size * sizeof(*k)));
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (k == NULL)
|
||||
goto exit;
|
||||
|
||||
/*----------------------------------------------- handle most of the key */
|
||||
while (size > 3) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 3;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*----------------------------------------- handle the last 3 uint32_t's */
|
||||
switch (size) {
|
||||
case 3 : c+=k[2];
|
||||
case 2 : b+=k[1];
|
||||
case 1 : a+=k[0];
|
||||
_JLU3_FINAL(a,b,c);
|
||||
/* fallthrough */
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
/*---------------------------------------------------- report the result */
|
||||
exit:
|
||||
return c;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32w) */
|
||||
|
||||
#if defined(_JLU3_jlu32l)
|
||||
uint32_t jlu32l(uint32_t h, const void *key, size_t size);
|
||||
/* -------------------------------------------------------------------- */
|
||||
/*
|
||||
* jlu32l() -- hash a variable-length key into a 32-bit value
|
||||
* h : can be any 4-byte value
|
||||
* k : the key (the unaligned variable-length array of bytes)
|
||||
* size : the size of the key, counting by bytes
|
||||
* Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
* the return value. Two keys differing by one or two bits will have
|
||||
* totally different hash values.
|
||||
*
|
||||
* The best hash table sizes are powers of 2. There is no need to do
|
||||
* mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
* use a bitmask. For example, if you need only 10 bits, do
|
||||
* h = (h & hashmask(10));
|
||||
* In which case, the hash table should have hashsize(10) elements.
|
||||
*
|
||||
* If you are hashing n strings (uint8_t **)k, do it like this:
|
||||
* for (i=0, h=0; i<n; ++i) h = jlu32l(h, k[i], len[i]);
|
||||
*
|
||||
* By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
|
||||
* code any way you wish, private, educational, or commercial. It's free.
|
||||
*
|
||||
* Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
* acceptable. Do NOT use for cryptographic purposes.
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *k the key, an array of uint8_t values
|
||||
* @param size the size of the key
|
||||
* @return the lookup3 hash
|
||||
*/
|
||||
/* -------------------------------------------------------------------- */
|
||||
uint32_t jlu32l(uint32_t h, const void *key, size_t size)
|
||||
{
|
||||
union { const void *ptr; size_t i; } u;
|
||||
uint32_t a = _JLU3_INIT(h, size);
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (key == NULL)
|
||||
goto exit;
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
#ifdef VALGRIND
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||
* then masks off the part it's not allowed to read. Because the
|
||||
* string is aligned, the masked-off tail is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticeably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef VALGRIND
|
||||
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||
case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += k[1]&0xffffff; a+=k[0]; break;
|
||||
case 6: b += k[1]&0xffff; a+=k[0]; break;
|
||||
case 5: b += k[1]&0xff; a+=k[0]; break;
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += k[0]&0xffffff; break;
|
||||
case 2: a += k[0]&0xffff; break;
|
||||
case 1: a += k[0]&0xff; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0] break;
|
||||
case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
|
||||
case 9: c += k8[8]; /* fallthrough */
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
|
||||
case 5: b += k8[4]; /* fallthrough */
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
|
||||
case 1: a += k8[0]; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#endif /* !valgrind */
|
||||
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*----------- all but last block: aligned reads and different mixing */
|
||||
while (size > 12) {
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12:
|
||||
c += k[4]+(((uint32_t)k[5])<<16);
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11:
|
||||
c += ((uint32_t)k8[10])<<16;
|
||||
/* fallthrough */
|
||||
case 10:
|
||||
c += (uint32_t)k[4];
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9:
|
||||
c += (uint32_t)k8[8];
|
||||
/* fallthrough */
|
||||
case 8:
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7:
|
||||
b += ((uint32_t)k8[6])<<16;
|
||||
/* fallthrough */
|
||||
case 6:
|
||||
b += (uint32_t)k[2];
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5:
|
||||
b += (uint32_t)k8[4];
|
||||
/* fallthrough */
|
||||
case 4:
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3:
|
||||
a += ((uint32_t)k8[2])<<16;
|
||||
/* fallthrough */
|
||||
case 2:
|
||||
a += (uint32_t)k[0];
|
||||
break;
|
||||
case 1:
|
||||
a += (uint32_t)k8[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*----------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += (uint32_t)k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += (uint32_t)k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += (uint32_t)k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*---------------------------- last block: affect all 32 bits of (c) */
|
||||
switch (size) {
|
||||
case 12: c += ((uint32_t)k[11])<<24; /* fallthrough */
|
||||
case 11: c += ((uint32_t)k[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k[9])<<8; /* fallthrough */
|
||||
case 9: c += (uint32_t)k[8]; /* fallthrough */
|
||||
case 8: b += ((uint32_t)k[7])<<24; /* fallthrough */
|
||||
case 7: b += ((uint32_t)k[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k[5])<<8; /* fallthrough */
|
||||
case 5: b += (uint32_t)k[4]; /* fallthrough */
|
||||
case 4: a += ((uint32_t)k[3])<<24; /* fallthrough */
|
||||
case 3: a += ((uint32_t)k[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k[1])<<8; /* fallthrough */
|
||||
case 1: a += (uint32_t)k[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
_JLU3_FINAL(a,b,c);
|
||||
|
||||
exit:
|
||||
return c;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32l) */
|
||||
|
||||
#if defined(_JLU3_jlu32lpair)
|
||||
/**
|
||||
* jlu32lpair: return 2 32-bit hash values.
|
||||
*
|
||||
* This is identical to jlu32l(), except it returns two 32-bit hash
|
||||
* values instead of just one. This is good enough for hash table
|
||||
* lookup with 2^^64 buckets, or if you want a second hash if you're not
|
||||
* happy with the first, or if you want a probably-unique 64-bit ID for
|
||||
* the key. *pc is better mixed than *pb, so use *pc first. If you want
|
||||
* a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *key the key, an array of uint8_t values
|
||||
* @param size the size of the key in bytes
|
||||
* @retval *pc, IN: primary initval, OUT: primary hash
|
||||
* *retval *pb IN: secondary initval, OUT: secondary hash
|
||||
*/
|
||||
void jlu32lpair(const void *key, size_t size, uint32_t *pc, uint32_t *pb)
|
||||
{
|
||||
union { const void *ptr; size_t i; } u;
|
||||
uint32_t a = _JLU3_INIT(*pc, size);
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (key == NULL)
|
||||
goto exit;
|
||||
|
||||
c += *pb; /* Add the secondary hash. */
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
#ifdef VALGRIND
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (size > (size_t)12) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 3;
|
||||
}
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||
* then masks off the part it's not allowed to read. Because the
|
||||
* string is aligned, the masked-off tail is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticeably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef VALGRIND
|
||||
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||
case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += k[1]&0xffffff; a+=k[0]; break;
|
||||
case 6: b += k[1]&0xffff; a+=k[0]; break;
|
||||
case 5: b += k[1]&0xff; a+=k[0]; break;
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += k[0]&0xffffff; break;
|
||||
case 2: a += k[0]&0xffff; break;
|
||||
case 1: a += k[0]&0xff; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
|
||||
case 9: c += k8[8]; /* fallthrough */
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
|
||||
case 5: b += k8[4]; /* fallthrough */
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
|
||||
case 1: a += k8[0]; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#endif /* !valgrind */
|
||||
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*----------- all but last block: aligned reads and different mixing */
|
||||
while (size > (size_t)12) {
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) {
|
||||
case 12:
|
||||
c += k[4]+(((uint32_t)k[5])<<16);
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11:
|
||||
c += ((uint32_t)k8[10])<<16;
|
||||
/* fallthrough */
|
||||
case 10:
|
||||
c += k[4];
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9:
|
||||
c += k8[8];
|
||||
/* fallthrough */
|
||||
case 8:
|
||||
b += k[2]+(((uint32_t)k[3])<<16);
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7:
|
||||
b += ((uint32_t)k8[6])<<16;
|
||||
/* fallthrough */
|
||||
case 6:
|
||||
b += k[2];
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5:
|
||||
b += k8[4];
|
||||
/* fallthrough */
|
||||
case 4:
|
||||
a += k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3:
|
||||
a += ((uint32_t)k8[2])<<16;
|
||||
/* fallthrough */
|
||||
case 2:
|
||||
a += k[0];
|
||||
break;
|
||||
case 1:
|
||||
a += k8[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*----------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (size > (size_t)12) {
|
||||
a += k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*---------------------------- last block: affect all 32 bits of (c) */
|
||||
switch (size) {
|
||||
case 12: c += ((uint32_t)k[11])<<24; /* fallthrough */
|
||||
case 11: c += ((uint32_t)k[10])<<16; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k[9])<<8; /* fallthrough */
|
||||
case 9: c += k[8]; /* fallthrough */
|
||||
case 8: b += ((uint32_t)k[7])<<24; /* fallthrough */
|
||||
case 7: b += ((uint32_t)k[6])<<16; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k[5])<<8; /* fallthrough */
|
||||
case 5: b += k[4]; /* fallthrough */
|
||||
case 4: a += ((uint32_t)k[3])<<24; /* fallthrough */
|
||||
case 3: a += ((uint32_t)k[2])<<16; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k[1])<<8; /* fallthrough */
|
||||
case 1: a += k[0];
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
_JLU3_FINAL(a,b,c);
|
||||
|
||||
exit:
|
||||
*pc = c;
|
||||
*pb = b;
|
||||
return;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32lpair) */
|
||||
|
||||
#if defined(_JLU3_jlu32b)
|
||||
uint32_t jlu32b(uint32_t h, const void *key, size_t size);
|
||||
/*
|
||||
* jlu32b():
|
||||
* This is the same as jlu32w() on big-endian machines. It is different
|
||||
* from jlu32l() on all machines. jlu32b() takes advantage of
|
||||
* big-endian byte ordering.
|
||||
*
|
||||
* @param h the previous hash, or an arbitrary value
|
||||
* @param *k the key, an array of uint8_t values
|
||||
* @param size the size of the key
|
||||
* @return the lookup3 hash
|
||||
*/
|
||||
uint32_t jlu32b(uint32_t h, const void *key, size_t size)
|
||||
{
|
||||
union { const void *ptr; size_t i; } u;
|
||||
uint32_t a = _JLU3_INIT(h, size);
|
||||
uint32_t b = a;
|
||||
uint32_t c = a;
|
||||
|
||||
if (key == NULL)
|
||||
return h;
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
#ifdef VALGRIND
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]<<8" actually reads beyond the end of the string, but
|
||||
* then shifts out the part it's not allowed to read. Because the
|
||||
* string is aligned, the illegal read is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticeably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef VALGRIND
|
||||
|
||||
switch (size) {
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c += k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
|
||||
case 9: c += k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += k[1]&0xffffff00; a+=k[0]; break;
|
||||
case 6: b += k[1]&0xffff0000; a+=k[0]; break;
|
||||
case 5: b += k[1]&0xff000000; a+=k[0]; break;
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += k[0]&0xffffff00; break;
|
||||
case 2: a += k[0]&0xffff0000; break;
|
||||
case 1: a += k[0]&0xff000000; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch (size) { /* all the case statements fall through */
|
||||
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c += ((uint32_t)k8[10])<<8; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k8[9])<<16; /* fallthrough */
|
||||
case 9: c += ((uint32_t)k8[8])<<24; /* fallthrough */
|
||||
case 8: b += k[1]; a+=k[0]; break;
|
||||
case 7: b += ((uint32_t)k8[6])<<8; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k8[5])<<16; /* fallthrough */
|
||||
case 5: b += ((uint32_t)k8[4])<<24; /* fallthrough */
|
||||
case 4: a += k[0]; break;
|
||||
case 3: a += ((uint32_t)k8[2])<<8; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k8[1])<<16; /* fallthrough */
|
||||
case 1: a += ((uint32_t)k8[0])<<24; break;
|
||||
case 0: goto exit;
|
||||
}
|
||||
|
||||
#endif /* !VALGRIND */
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*----------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (size > 12) {
|
||||
a += ((uint32_t)k[0])<<24;
|
||||
a += ((uint32_t)k[1])<<16;
|
||||
a += ((uint32_t)k[2])<<8;
|
||||
a += ((uint32_t)k[3]);
|
||||
b += ((uint32_t)k[4])<<24;
|
||||
b += ((uint32_t)k[5])<<16;
|
||||
b += ((uint32_t)k[6])<<8;
|
||||
b += ((uint32_t)k[7]);
|
||||
c += ((uint32_t)k[8])<<24;
|
||||
c += ((uint32_t)k[9])<<16;
|
||||
c += ((uint32_t)k[10])<<8;
|
||||
c += ((uint32_t)k[11]);
|
||||
_JLU3_MIX(a,b,c);
|
||||
size -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*---------------------------- last block: affect all 32 bits of (c) */
|
||||
switch (size) { /* all the case statements fall through */
|
||||
case 12: c += k[11]; /* fallthrough */
|
||||
case 11: c += ((uint32_t)k[10])<<8; /* fallthrough */
|
||||
case 10: c += ((uint32_t)k[9])<<16; /* fallthrough */
|
||||
case 9: c += ((uint32_t)k[8])<<24; /* fallthrough */
|
||||
case 8: b += k[7]; /* fallthrough */
|
||||
case 7: b += ((uint32_t)k[6])<<8; /* fallthrough */
|
||||
case 6: b += ((uint32_t)k[5])<<16; /* fallthrough */
|
||||
case 5: b += ((uint32_t)k[4])<<24; /* fallthrough */
|
||||
case 4: a += k[3]; /* fallthrough */
|
||||
case 3: a += ((uint32_t)k[2])<<8; /* fallthrough */
|
||||
case 2: a += ((uint32_t)k[1])<<16; /* fallthrough */
|
||||
case 1: a += ((uint32_t)k[0])<<24; /* fallthrough */
|
||||
break;
|
||||
case 0:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
_JLU3_FINAL(a,b,c);
|
||||
|
||||
exit:
|
||||
return c;
|
||||
}
|
||||
#endif /* defined(_JLU3_jlu32b) */
|
||||
|
||||
#if defined(_JLU3_SELFTEST)
|
||||
|
||||
/* used for timings */
|
||||
static void driver1(void)
|
||||
{
|
||||
uint8_t buf[256];
|
||||
uint32_t i;
|
||||
uint32_t h=0;
|
||||
time_t a,z;
|
||||
|
||||
time(&a);
|
||||
for (i=0; i<256; ++i) buf[i] = 'x';
|
||||
for (i=0; i<1; ++i) {
|
||||
h = jlu32l(h, &buf[0], sizeof(buf[0]));
|
||||
}
|
||||
time(&z);
|
||||
if (z-a > 0) printf("time %d %.8x\n", (int)(z-a), h);
|
||||
}
|
||||
|
||||
/* check that every input bit changes every output bit half the time */
|
||||
#define HASHSTATE 1
|
||||
#define HASHLEN 1
|
||||
#define MAXPAIR 60
|
||||
#define MAXLEN 70
|
||||
static void driver2(void)
|
||||
{
|
||||
uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
|
||||
uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
|
||||
uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
|
||||
uint32_t x[HASHSTATE],y[HASHSTATE];
|
||||
uint32_t hlen;
|
||||
|
||||
printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
|
||||
for (hlen=0; hlen < MAXLEN; ++hlen) {
|
||||
z=0;
|
||||
for (i=0; i<hlen; ++i) { /*-------------- for each input byte, */
|
||||
for (j=0; j<8; ++j) { /*--------------- for each input bit, */
|
||||
for (m=1; m<8; ++m) { /*---- for several possible initvals, */
|
||||
for (l=0; l<HASHSTATE; ++l)
|
||||
e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);
|
||||
|
||||
/* check that every output bit is affected by that input bit */
|
||||
for (k=0; k<MAXPAIR; k+=2) {
|
||||
uint32_t finished=1;
|
||||
/* keys have one bit different */
|
||||
for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;}
|
||||
/* have a and b be two keys differing in only one bit */
|
||||
a[i] ^= (k<<j);
|
||||
a[i] ^= (k>>(8-j));
|
||||
c[0] = jlu32l(m, a, hlen);
|
||||
b[i] ^= ((k+1)<<j);
|
||||
b[i] ^= ((k+1)>>(8-j));
|
||||
d[0] = jlu32l(m, b, hlen);
|
||||
/* check every bit is 1, 0, set, and not set at least once */
|
||||
for (l=0; l<HASHSTATE; ++l) {
|
||||
e[l] &= (c[l]^d[l]);
|
||||
f[l] &= ~(c[l]^d[l]);
|
||||
g[l] &= c[l];
|
||||
h[l] &= ~c[l];
|
||||
x[l] &= d[l];
|
||||
y[l] &= ~d[l];
|
||||
if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0;
|
||||
}
|
||||
if (finished) break;
|
||||
}
|
||||
if (k>z) z=k;
|
||||
if (k == MAXPAIR) {
|
||||
printf("Some bit didn't change: ");
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x ",
|
||||
e[0],f[0],g[0],h[0],x[0],y[0]);
|
||||
printf("i %u j %u m %u len %u\n", i, j, m, hlen);
|
||||
}
|
||||
if (z == MAXPAIR) goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (z < MAXPAIR) {
|
||||
printf("Mix success %2u bytes %2u initvals ",i,m);
|
||||
printf("required %u trials\n", z/2);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Check for reading beyond the end of the buffer and alignment problems */
|
||||
static void driver3(void)
|
||||
{
|
||||
uint8_t buf[MAXLEN+20], *b;
|
||||
uint32_t len;
|
||||
uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
|
||||
uint32_t h;
|
||||
uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
|
||||
uint32_t i;
|
||||
uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
|
||||
uint32_t j;
|
||||
uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
|
||||
uint32_t ref,x,y;
|
||||
uint8_t *p;
|
||||
uint32_t m = 13;
|
||||
|
||||
printf("Endianness. These lines should all be the same (for values filled in):\n");
|
||||
printf("%.8x %.8x %.8x\n",
|
||||
jlu32w(m, (const uint32_t *)q, (sizeof(q)-1)/4),
|
||||
jlu32w(m, (const uint32_t *)q, (sizeof(q)-5)/4),
|
||||
jlu32w(m, (const uint32_t *)q, (sizeof(q)-9)/4));
|
||||
p = q;
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
p = &qq[1];
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
p = &qqq[2];
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
p = &qqqq[3];
|
||||
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
|
||||
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
|
||||
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
|
||||
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
|
||||
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
|
||||
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
|
||||
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
|
||||
printf("\n");
|
||||
for (h=0, b=buf+1; h<8; ++h, ++b) {
|
||||
for (i=0; i<MAXLEN; ++i) {
|
||||
len = i;
|
||||
for (j=0; j<i; ++j)
|
||||
*(b+j)=0;
|
||||
|
||||
/* these should all be equal */
|
||||
m = 1;
|
||||
ref = jlu32l(m, b, len);
|
||||
*(b+i)=(uint8_t)~0;
|
||||
*(b-1)=(uint8_t)~0;
|
||||
x = jlu32l(m, b, len);
|
||||
y = jlu32l(m, b, len);
|
||||
if ((ref != x) || (ref != y))
|
||||
printf("alignment error: %.8x %.8x %.8x %u %u\n",ref,x,y, h, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check for problems with nulls */
|
||||
static void driver4(void)
|
||||
{
|
||||
uint8_t buf[1];
|
||||
uint32_t h;
|
||||
uint32_t i;
|
||||
uint32_t state[HASHSTATE];
|
||||
|
||||
buf[0] = ~0;
|
||||
for (i=0; i<HASHSTATE; ++i)
|
||||
state[i] = 1;
|
||||
printf("These should all be different\n");
|
||||
h = 0;
|
||||
for (i=0; i<8; ++i) {
|
||||
h = jlu32l(h, buf, 0);
|
||||
printf("%2ld 0-byte strings, hash is %.8x\n", (long)i, h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
driver1(); /* test that the key is hashed: used for timings */
|
||||
driver2(); /* test that whole key is hashed thoroughly */
|
||||
driver3(); /* test that nothing but the key is hashed */
|
||||
driver4(); /* test hashing multiple buffers (all buffers are null) */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* _JLU3_SELFTEST */
|
||||
1483
popt/popt.c
1483
popt/popt.c
File diff suppressed because it is too large
Load Diff
473
popt/popt.h
473
popt/popt.h
@@ -1,5 +1,4 @@
|
||||
/** \file popt/popt.h
|
||||
* \ingroup popt
|
||||
/** @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -13,45 +12,49 @@
|
||||
|
||||
#define POPT_OPTION_DEPTH 10
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Arg type identifiers
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_ARG_NONE 0 /*!< no arg */
|
||||
#define POPT_ARG_STRING 1 /*!< arg will be saved as string */
|
||||
#define POPT_ARG_INT 2 /*!< arg will be converted to int */
|
||||
#define POPT_ARG_LONG 3 /*!< arg will be converted to long */
|
||||
#define POPT_ARG_INCLUDE_TABLE 4 /*!< arg points to table */
|
||||
#define POPT_ARG_CALLBACK 5 /*!< table-wide callback... must be
|
||||
#define POPT_ARG_NONE 0U /*!< no arg */
|
||||
#define POPT_ARG_STRING 1U /*!< arg will be saved as string */
|
||||
#define POPT_ARG_INT 2U /*!< arg ==> int */
|
||||
#define POPT_ARG_LONG 3U /*!< arg ==> long */
|
||||
#define POPT_ARG_INCLUDE_TABLE 4U /*!< arg points to table */
|
||||
#define POPT_ARG_CALLBACK 5U /*!< table-wide callback... must be
|
||||
set first in table; arg points
|
||||
to callback, descrip points to
|
||||
callback data to pass */
|
||||
#define POPT_ARG_INTL_DOMAIN 6 /*!< set the translation domain
|
||||
#define POPT_ARG_INTL_DOMAIN 6U /*!< set the translation domain
|
||||
for this table and any
|
||||
included tables; arg points
|
||||
to the domain string */
|
||||
#define POPT_ARG_VAL 7 /*!< arg should take value val */
|
||||
#define POPT_ARG_FLOAT 8 /*!< arg will be converted to float */
|
||||
#define POPT_ARG_DOUBLE 9 /*!< arg will be converted to double */
|
||||
#define POPT_ARG_VAL 7U /*!< arg should take value val */
|
||||
#define POPT_ARG_FLOAT 8U /*!< arg ==> float */
|
||||
#define POPT_ARG_DOUBLE 9U /*!< arg ==> double */
|
||||
#define POPT_ARG_LONGLONG 10U /*!< arg ==> long long */
|
||||
|
||||
#define POPT_ARG_MASK 0x0000FFFF
|
||||
/*@}*/
|
||||
#define POPT_ARG_MAINCALL (16U+11U) /*!< EXPERIMENTAL: return (*arg) (argc, argv) */
|
||||
#define POPT_ARG_ARGV 12U /*!< dupe'd arg appended to realloc'd argv array. */
|
||||
#define POPT_ARG_SHORT 13U /*!< arg ==> short */
|
||||
#define POPT_ARG_BITSET (16U+14U) /*!< arg ==> bit set */
|
||||
|
||||
/** \ingroup popt
|
||||
#define POPT_ARG_MASK 0x000000FFU
|
||||
#define POPT_GROUP_MASK 0x0000FF00U
|
||||
|
||||
/**
|
||||
* \name Arg modifiers
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_ARGFLAG_ONEDASH 0x80000000 /*!< allow -longoption */
|
||||
#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /*!< don't show in help/usage */
|
||||
#define POPT_ARGFLAG_STRIP 0x20000000 /*!< strip this arg from argv(only applies to long args) */
|
||||
#define POPT_ARGFLAG_OPTIONAL 0x10000000 /*!< arg may be missing */
|
||||
#define POPT_ARGFLAG_ONEDASH 0x80000000U /*!< allow -longoption */
|
||||
#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000U /*!< don't show in help/usage */
|
||||
#define POPT_ARGFLAG_STRIP 0x20000000U /*!< strip this arg from argv(only applies to long args) */
|
||||
#define POPT_ARGFLAG_OPTIONAL 0x10000000U /*!< arg may be missing */
|
||||
|
||||
#define POPT_ARGFLAG_OR 0x08000000 /*!< arg will be or'ed */
|
||||
#define POPT_ARGFLAG_NOR 0x09000000 /*!< arg will be nor'ed */
|
||||
#define POPT_ARGFLAG_AND 0x04000000 /*!< arg will be and'ed */
|
||||
#define POPT_ARGFLAG_NAND 0x05000000 /*!< arg will be nand'ed */
|
||||
#define POPT_ARGFLAG_XOR 0x02000000 /*!< arg will be xor'ed */
|
||||
#define POPT_ARGFLAG_NOT 0x01000000 /*!< arg will be negated */
|
||||
#define POPT_ARGFLAG_OR 0x08000000U /*!< arg will be or'ed */
|
||||
#define POPT_ARGFLAG_NOR 0x09000000U /*!< arg will be nor'ed */
|
||||
#define POPT_ARGFLAG_AND 0x04000000U /*!< arg will be and'ed */
|
||||
#define POPT_ARGFLAG_NAND 0x05000000U /*!< arg will be nand'ed */
|
||||
#define POPT_ARGFLAG_XOR 0x02000000U /*!< arg will be xor'ed */
|
||||
#define POPT_ARGFLAG_NOT 0x01000000U /*!< arg will be negated */
|
||||
#define POPT_ARGFLAG_LOGICALOPS \
|
||||
(POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
|
||||
|
||||
@@ -60,158 +63,126 @@
|
||||
#define POPT_BIT_CLR (POPT_ARG_VAL|POPT_ARGFLAG_NAND)
|
||||
/*!< clear arg bit(s) */
|
||||
|
||||
#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
|
||||
#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000U /*!< show default value in --help */
|
||||
#define POPT_ARGFLAG_RANDOM 0x00400000U /*!< random value in [1,arg] */
|
||||
#define POPT_ARGFLAG_TOGGLE 0x00200000U /*!< permit --[no]opt prefix toggle */
|
||||
|
||||
/*@}*/
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Callback modifiers
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_CBFLAG_PRE 0x80000000 /*!< call the callback before parse */
|
||||
#define POPT_CBFLAG_POST 0x40000000 /*!< call the callback after parse */
|
||||
#define POPT_CBFLAG_INC_DATA 0x20000000 /*!< use data from the include line,
|
||||
#define POPT_CBFLAG_PRE 0x80000000U /*!< call the callback before parse */
|
||||
#define POPT_CBFLAG_POST 0x40000000U /*!< call the callback after parse */
|
||||
#define POPT_CBFLAG_INC_DATA 0x20000000U /*!< use data from the include line,
|
||||
not the subtable */
|
||||
#define POPT_CBFLAG_SKIPOPTION 0x10000000 /*!< don't callback with option */
|
||||
#define POPT_CBFLAG_CONTINUE 0x08000000 /*!< continue callbacks with option */
|
||||
/*@}*/
|
||||
#define POPT_CBFLAG_SKIPOPTION 0x10000000U /*!< don't callback with option */
|
||||
#define POPT_CBFLAG_CONTINUE 0x08000000U /*!< continue callbacks with option */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Error return values
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_ERROR_NOARG -10 /*!< missing argument */
|
||||
#define POPT_ERROR_BADOPT -11 /*!< unknown option */
|
||||
#define POPT_ERROR_UNWANTEDARG -12 /*!< option does not take an argument */
|
||||
#define POPT_ERROR_OPTSTOODEEP -13 /*!< aliases nested too deeply */
|
||||
#define POPT_ERROR_BADQUOTE -15 /*!< error in paramter quoting */
|
||||
#define POPT_ERROR_BADQUOTE -15 /*!< error in parameter quoting */
|
||||
#define POPT_ERROR_ERRNO -16 /*!< errno set, use strerror(errno) */
|
||||
#define POPT_ERROR_BADNUMBER -17 /*!< invalid numeric value */
|
||||
#define POPT_ERROR_OVERFLOW -18 /*!< number too large or too small */
|
||||
#define POPT_ERROR_BADOPERATION -19 /*!< mutually exclusive logical operations requested */
|
||||
#define POPT_ERROR_NULLARG -20 /*!< opt->arg should not be NULL */
|
||||
#define POPT_ERROR_MALLOC -21 /*!< memory allocation failed */
|
||||
/*@}*/
|
||||
#define POPT_ERROR_BADCONFIG -22 /*!< config file failed sanity test */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name poptBadOption() flags
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_BADOPTION_NOALIAS (1 << 0) /*!< don't go into an alias */
|
||||
/*@}*/
|
||||
#define POPT_BADOPTION_NOALIAS (1U << 0) /*!< don't go into an alias */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name poptGetContext() flags
|
||||
*/
|
||||
/*@{*/
|
||||
#define POPT_CONTEXT_NO_EXEC (1 << 0) /*!< ignore exec expansions */
|
||||
#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /*!< pay attention to argv[0] */
|
||||
#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
|
||||
#define POPT_CONTEXT_ARG_OPTS (1 << 4) /*!< return args as options with value 0 */
|
||||
/*@}*/
|
||||
#define POPT_CONTEXT_NO_EXEC (1U << 0) /*!< ignore exec expansions */
|
||||
#define POPT_CONTEXT_KEEP_FIRST (1U << 1) /*!< pay attention to argv[0] */
|
||||
#define POPT_CONTEXT_POSIXMEHARDER (1U << 2) /*!< options can't follow args */
|
||||
#define POPT_CONTEXT_ARG_OPTS (1U << 4) /*!< return args as options with value 0 */
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
*/
|
||||
struct poptOption {
|
||||
/*@observer@*/ /*@null@*/
|
||||
const char * longName; /*!< may be NULL */
|
||||
char shortName; /*!< may be NUL */
|
||||
int argInfo;
|
||||
/*@shared@*/ /*@null@*/
|
||||
char shortName; /*!< may be '\0' */
|
||||
unsigned int argInfo; /*!< type of argument expected after the option */
|
||||
void * arg; /*!< depends on argInfo */
|
||||
int val; /*!< 0 means don't return, just update flag */
|
||||
/*@observer@*/ /*@null@*/
|
||||
int val; /*!< 0 means don't return, just update arg */
|
||||
const char * descrip; /*!< description for autohelp -- may be NULL */
|
||||
/*@observer@*/ /*@null@*/
|
||||
const char * argDescrip; /*!< argument description for autohelp */
|
||||
const char * argDescrip; /*!< argument description for autohelp -- may be NULL */
|
||||
};
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* A popt alias argument for poptAddAlias().
|
||||
*/
|
||||
struct poptAlias {
|
||||
/*@owned@*/ /*@null@*/
|
||||
const char * longName; /*!< may be NULL */
|
||||
char shortName; /*!< may be NUL */
|
||||
int argc;
|
||||
/*@owned@*/
|
||||
const char ** argv; /*!< must be free()able */
|
||||
};
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* A popt alias or exec argument for poptAddItem().
|
||||
*/
|
||||
/*@-exporttype@*/
|
||||
typedef struct poptItem_s {
|
||||
struct poptOption option; /*!< alias/exec name(s) and description. */
|
||||
int argc; /*!< (alias) no. of args. */
|
||||
/*@owned@*/
|
||||
const char ** argv; /*!< (alias) args, must be free()able. */
|
||||
} * poptItem;
|
||||
/*@=exporttype@*/
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* \name Auto-generated help/usage
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* Empty table marker to enable displaying popt alias/exec options.
|
||||
*/
|
||||
/*@-exportvar@*/
|
||||
/*@unchecked@*/ /*@observer@*/
|
||||
extern struct poptOption poptAliasOptions[];
|
||||
/*@=exportvar@*/
|
||||
#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
|
||||
0, "Options implemented via popt alias/exec:", NULL },
|
||||
|
||||
/**
|
||||
* Auto help table options.
|
||||
*/
|
||||
/*@-exportvar@*/
|
||||
/*@unchecked@*/ /*@observer@*/
|
||||
extern struct poptOption poptHelpOptions[];
|
||||
/*@=exportvar@*/
|
||||
|
||||
/*@-exportvar@*/
|
||||
/*@unchecked@*/ /*@observer@*/
|
||||
extern struct poptOption * poptHelpOptionsI18N;
|
||||
/*@=exportvar@*/
|
||||
|
||||
#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
|
||||
0, "Help options:", NULL },
|
||||
|
||||
#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
|
||||
/*@}*/
|
||||
#define POPT_TABLEEND { NULL, '\0', 0, NULL, 0, NULL, NULL }
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
*/
|
||||
/*@-exporttype@*/
|
||||
typedef /*@abstract@*/ struct poptContext_s * poptContext;
|
||||
/*@=exporttype@*/
|
||||
typedef struct poptContext_s * poptContext;
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
*/
|
||||
#ifndef __cplusplus
|
||||
/*@-exporttype -typeuse@*/
|
||||
typedef struct poptOption * poptOption;
|
||||
/*@=exporttype =typeuse@*/
|
||||
#endif
|
||||
|
||||
/*@-exportconst@*/
|
||||
/**
|
||||
*/
|
||||
enum poptCallbackReason {
|
||||
POPT_CALLBACK_REASON_PRE = 0,
|
||||
POPT_CALLBACK_REASON_POST = 1,
|
||||
POPT_CALLBACK_REASON_OPTION = 2
|
||||
};
|
||||
/*@=exportconst@*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*@-type@*/
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Table callback prototype.
|
||||
* @param con context
|
||||
* @param reason reason for callback
|
||||
@@ -221,13 +192,18 @@ extern "C" {
|
||||
*/
|
||||
typedef void (*poptCallbackType) (poptContext con,
|
||||
enum poptCallbackReason reason,
|
||||
/*@null@*/ const struct poptOption * opt,
|
||||
/*@null@*/ const char * arg,
|
||||
/*@null@*/ const void * data)
|
||||
/*@globals internalState @*/
|
||||
/*@modifies internalState @*/;
|
||||
const struct poptOption * opt,
|
||||
const char * arg,
|
||||
const void * data);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Destroy context.
|
||||
* @param con context
|
||||
* @return NULL always
|
||||
*/
|
||||
poptContext poptFreeContext( poptContext con);
|
||||
|
||||
/**
|
||||
* Initialize popt context.
|
||||
* @param name context name (usually argv[0] program name)
|
||||
* @param argc no. of arguments
|
||||
@@ -236,97 +212,90 @@ typedef void (*poptCallbackType) (poptContext con,
|
||||
* @param flags or'd POPT_CONTEXT_* bits
|
||||
* @return initialized popt context
|
||||
*/
|
||||
/*@only@*/ /*@null@*/
|
||||
poptContext poptGetContext(
|
||||
/*@dependent@*/ /*@keep@*/ const char * name,
|
||||
int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
|
||||
/*@dependent@*/ /*@keep@*/ const struct poptOption * options,
|
||||
int flags)
|
||||
/*@*/;
|
||||
const char * name,
|
||||
int argc, const char ** argv,
|
||||
const struct poptOption * options,
|
||||
unsigned int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Destroy context (alternative implementation).
|
||||
* @param con context
|
||||
* @return NULL always
|
||||
*/
|
||||
poptContext poptFini( poptContext con);
|
||||
|
||||
/**
|
||||
* Initialize popt context (alternative implementation).
|
||||
* This routine does poptGetContext() and then poptReadConfigFiles().
|
||||
* @param argc no. of arguments
|
||||
* @param argv argument array
|
||||
* @param options address of popt option table
|
||||
* @param configPaths colon separated file path(s) to read.
|
||||
* @return initialized popt context (NULL on error).
|
||||
*/
|
||||
poptContext poptInit(int argc, const char ** argv,
|
||||
const struct poptOption * options,
|
||||
const char * configPaths);
|
||||
|
||||
/**
|
||||
* Reinitialize popt context.
|
||||
* @param con context
|
||||
*/
|
||||
/*@unused@*/
|
||||
void poptResetContext(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
void poptResetContext(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return value of next option found.
|
||||
* @param con context
|
||||
* @return next option val, -1 on last item, POPT_ERROR_* on error
|
||||
*/
|
||||
int poptGetNextOpt(/*@null@*/poptContext con)
|
||||
/*@globals fileSystem, internalState @*/
|
||||
/*@modifies con, fileSystem, internalState @*/;
|
||||
int poptGetNextOpt(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return next option argument (if any).
|
||||
* @param con context
|
||||
* @return option argument, NULL if no argument is available
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/ /*@unused@*/
|
||||
const char * poptGetOptArg(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
char * poptGetOptArg(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return next argument.
|
||||
* @param con context
|
||||
* @return next argument, NULL if no argument is available
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/ /*@unused@*/
|
||||
const char * poptGetArg(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
const char * poptGetArg(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Peek at current argument.
|
||||
* @param con context
|
||||
* @return current argument, NULL if no argument is available
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/ /*@unused@*/
|
||||
const char * poptPeekArg(/*@null@*/poptContext con)
|
||||
/*@*/;
|
||||
const char * poptPeekArg(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return remaining arguments.
|
||||
* @param con context
|
||||
* @return argument array, NULL terminated
|
||||
*/
|
||||
/*@observer@*/ /*@null@*/
|
||||
const char ** poptGetArgs(/*@null@*/poptContext con)
|
||||
/*@modifies con @*/;
|
||||
const char ** poptGetArgs(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return the option which caused the most recent error.
|
||||
* @param con context
|
||||
* @param flags
|
||||
* @return offending option
|
||||
*/
|
||||
/*@observer@*/
|
||||
const char * poptBadOption(/*@null@*/poptContext con, int flags)
|
||||
/*@*/;
|
||||
const char * poptBadOption(poptContext con, unsigned int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
* Destroy context.
|
||||
* @param con context
|
||||
* @return NULL always
|
||||
*/
|
||||
/*@null@*/
|
||||
poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
|
||||
/*@modifies con @*/;
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Add arguments to context.
|
||||
* @param con context
|
||||
* @param argv argument array, NULL terminated
|
||||
* @return 0 on success, POPT_ERROR_OPTSTOODEEP on failure
|
||||
*/
|
||||
/*@unused@*/
|
||||
int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
|
||||
/*@modifies con @*/;
|
||||
int poptStuffArgs(poptContext con, const char ** argv);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Add alias to context.
|
||||
* @todo Pass alias by reference, not value.
|
||||
* @deprecated Use poptAddItem instead.
|
||||
@@ -335,44 +304,64 @@ int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
|
||||
* @param flags (unused)
|
||||
* @return 0 on success
|
||||
*/
|
||||
/*@unused@*/
|
||||
int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
|
||||
/*@modifies con @*/;
|
||||
int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Add alias/exec item to context.
|
||||
* @param con context
|
||||
* @param newItem alias/exec item to add
|
||||
* @param flags 0 for alias, 1 for exec
|
||||
* @return 0 on success
|
||||
*/
|
||||
int poptAddItem(poptContext con, poptItem newItem, int flags)
|
||||
/*@modifies con @*/;
|
||||
int poptAddItem(poptContext con, poptItem newItem, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Test path/file for config file sanity (regular file, permissions etc)
|
||||
* @param fn file name
|
||||
* @return 1 on OK, 0 on NOTOK.
|
||||
*/
|
||||
int poptSaneFile(const char * fn);
|
||||
|
||||
/**
|
||||
* Read a file into a buffer.
|
||||
* @param fn file name
|
||||
* @retval *bp buffer (malloc'd) (or NULL)
|
||||
* @retval *nbp no. of bytes in buffer (including final NUL) (or NULL)
|
||||
* @param flags 1 to trim escaped newlines
|
||||
* return 0 on success
|
||||
*/
|
||||
int poptReadFile(const char * fn, char ** bp,
|
||||
size_t * nbp, int flags);
|
||||
#define POPT_READFILE_TRIMNEWLINES 1
|
||||
|
||||
/**
|
||||
* Read configuration file.
|
||||
* @param con context
|
||||
* @param fn file name to read
|
||||
* @return 0 on success, POPT_ERROR_ERRNO on failure
|
||||
*/
|
||||
int poptReadConfigFile(poptContext con, const char * fn)
|
||||
/*@globals errno, fileSystem, internalState @*/
|
||||
/*@modifies con->execs, con->numExecs,
|
||||
errno, fileSystem, internalState @*/;
|
||||
int poptReadConfigFile(poptContext con, const char * fn);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Read configuration file(s).
|
||||
* Colon separated files to read, looping over poptReadConfigFile().
|
||||
* Note that an '@' character preceding a path in the list will
|
||||
* also perform additional sanity checks on the file before reading.
|
||||
* @param con context
|
||||
* @param paths colon separated file name(s) to read
|
||||
* @return 0 on success, POPT_ERROR_BADCONFIG on failure
|
||||
*/
|
||||
int poptReadConfigFiles(poptContext con, const char * paths);
|
||||
|
||||
/**
|
||||
* Read default configuration from /etc/popt and $HOME/.popt.
|
||||
* @param con context
|
||||
* @param useEnv (unused)
|
||||
* @return 0 on success, POPT_ERROR_ERRNO on failure
|
||||
*/
|
||||
/*@unused@*/
|
||||
int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
|
||||
/*@globals fileSystem, internalState @*/
|
||||
/*@modifies con->execs, con->numExecs,
|
||||
fileSystem, internalState @*/;
|
||||
int poptReadDefaultConfig(poptContext con, int useEnv);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Duplicate an argument array.
|
||||
* @note: The argument array is malloc'd as a single area, so only argv must
|
||||
* be free'd.
|
||||
@@ -383,12 +372,11 @@ int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
|
||||
* @retval argvPtr address of returned argument array
|
||||
* @return 0 on success, POPT_ERROR_NOARG on failure
|
||||
*/
|
||||
int poptDupArgv(int argc, /*@null@*/ const char **argv,
|
||||
/*@null@*/ /*@out@*/ int * argcPtr,
|
||||
/*@null@*/ /*@out@*/ const char *** argvPtr)
|
||||
/*@modifies *argcPtr, *argvPtr @*/;
|
||||
int poptDupArgv(int argc, const char **argv,
|
||||
int * argcPtr,
|
||||
const char *** argvPtr);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Parse a string into an argument array.
|
||||
* The parse allows ', ", and \ quoting, but ' is treated the same as " and
|
||||
* both may include \ quotes.
|
||||
@@ -400,10 +388,9 @@ int poptDupArgv(int argc, /*@null@*/ const char **argv,
|
||||
* @retval argvPtr address of returned argument array
|
||||
*/
|
||||
int poptParseArgvString(const char * s,
|
||||
/*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
|
||||
/*@modifies *argcPtr, *argvPtr @*/;
|
||||
int * argcPtr, const char *** argvPtr);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Parses an input configuration file and returns an string that is a
|
||||
* command line. For use with popt. You must free the return value when done.
|
||||
*
|
||||
@@ -418,8 +405,8 @@ bla=bla
|
||||
|
||||
this_is = fdsafdas
|
||||
bad_line=
|
||||
reall bad line
|
||||
reall bad line = again
|
||||
really bad line
|
||||
really bad line = again
|
||||
5555= 55555
|
||||
test = with lots of spaces
|
||||
\endverbatim
|
||||
@@ -449,83 +436,82 @@ this_is = fdsafdas
|
||||
* @return 0 on success
|
||||
* @see poptParseArgvString
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
|
||||
/*@globals fileSystem @*/
|
||||
/*@modifies *fp, *argstrp, fileSystem @*/;
|
||||
/*@=fcnuse@*/
|
||||
int poptConfigFileToString(FILE *fp, char ** argstrp, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return formatted error string for popt failure.
|
||||
* @param error popt error
|
||||
* @return error string
|
||||
*/
|
||||
/*@observer@*/
|
||||
const char * poptStrerror(const int error)
|
||||
/*@*/;
|
||||
const char * poptStrerror(const int error);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Limit search for executables.
|
||||
* @param con context
|
||||
* @param path single path to search for executables
|
||||
* @param allowAbsolute absolute paths only?
|
||||
*/
|
||||
/*@unused@*/
|
||||
void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
|
||||
/*@modifies con @*/;
|
||||
void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Print detailed description of options.
|
||||
* @param con context
|
||||
* @param fp ouput file handle
|
||||
* @param fp output file handle
|
||||
* @param flags (unused)
|
||||
*/
|
||||
void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
|
||||
/*@globals fileSystem @*/
|
||||
/*@modifies *fp, fileSystem @*/;
|
||||
void poptPrintHelp(poptContext con, FILE * fp, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Print terse description of options.
|
||||
* @param con context
|
||||
* @param fp ouput file handle
|
||||
* @param fp output file handle
|
||||
* @param flags (unused)
|
||||
*/
|
||||
void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
|
||||
/*@globals fileSystem @*/
|
||||
/*@modifies *fp, fileSystem @*/;
|
||||
void poptPrintUsage(poptContext con, FILE * fp, int flags);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Provide text to replace default "[OPTION...]" in help/usage output.
|
||||
* @param con context
|
||||
* @param text replacement text
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
void poptSetOtherOptionHelp(poptContext con, const char * text)
|
||||
/*@modifies con @*/;
|
||||
/*@=fcnuse@*/
|
||||
void poptSetOtherOptionHelp(poptContext con, const char * text);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Return argv[0] from context.
|
||||
* @param con context
|
||||
* @return argv[0]
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
/*@observer@*/
|
||||
const char * poptGetInvocationName(poptContext con)
|
||||
/*@*/;
|
||||
/*@=fcnuse@*/
|
||||
const char * poptGetInvocationName(poptContext con);
|
||||
|
||||
/** \ingroup popt
|
||||
/**
|
||||
* Shuffle argv pointers to remove stripped args, returns new argc.
|
||||
* @param con context
|
||||
* @param argc no. of args
|
||||
* @param argv arg vector
|
||||
* @return new argc
|
||||
*/
|
||||
/*@-fcnuse@*/
|
||||
int poptStrippedArgv(poptContext con, int argc, char ** argv)
|
||||
/*@modifies *argv @*/;
|
||||
/*@=fcnuse@*/
|
||||
int poptStrippedArgv(poptContext con, int argc, char ** argv);
|
||||
|
||||
/**
|
||||
* Add a string to an argv array.
|
||||
* @retval *argvp argv array
|
||||
* @param argInfo (unused)
|
||||
* @param val string arg to add (using strdup)
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveString(const char *** argvp, unsigned int argInfo,
|
||||
const char * val);
|
||||
|
||||
/**
|
||||
* Save a long long, performing logical operation with value.
|
||||
* @warning Alignment check may be too strict on certain platorms.
|
||||
* @param arg integer pointer, aligned on int boundary.
|
||||
* @param argInfo logical operation (see POPT_ARGFLAG_*)
|
||||
* @param aLongLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveLongLong(long long * arg, unsigned int argInfo,
|
||||
long long aLongLong);
|
||||
|
||||
/**
|
||||
* Save a long, performing logical operation with value.
|
||||
@@ -535,12 +521,17 @@ int poptStrippedArgv(poptContext con, int argc, char ** argv)
|
||||
* @param aLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
/*@-incondefs@*/
|
||||
/*@unused@*/
|
||||
int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
|
||||
/*@modifies *arg @*/
|
||||
/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
|
||||
/*@=incondefs@*/
|
||||
int poptSaveLong(long * arg, unsigned int argInfo, long aLong);
|
||||
|
||||
/**
|
||||
* Save a short integer, performing logical operation with value.
|
||||
* @warning Alignment check may be too strict on certain platorms.
|
||||
* @param arg short pointer, aligned on short boundary.
|
||||
* @param argInfo logical operation (see POPT_ARGFLAG_*)
|
||||
* @param aLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveShort(short * arg, unsigned int argInfo, long aLong);
|
||||
|
||||
/**
|
||||
* Save an integer, performing logical operation with value.
|
||||
@@ -550,14 +541,40 @@ int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
|
||||
* @param aLong value to use
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
/*@-incondefs@*/
|
||||
/*@unused@*/
|
||||
int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
|
||||
/*@modifies *arg @*/
|
||||
/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
|
||||
/*@=incondefs@*/
|
||||
int poptSaveInt(int * arg, unsigned int argInfo, long aLong);
|
||||
|
||||
/* The bit set typedef. */
|
||||
typedef struct poptBits_s {
|
||||
unsigned int bits[1];
|
||||
} * poptBits;
|
||||
|
||||
#define _POPT_BITS_N 1024U /*!< estimated population */
|
||||
#define _POPT_BITS_M ((3U * _POPT_BITS_N) / 2U)
|
||||
#define _POPT_BITS_K 16U /*!< no. of linear hash combinations */
|
||||
|
||||
extern unsigned int _poptBitsN;
|
||||
extern unsigned int _poptBitsM;
|
||||
extern unsigned int _poptBitsK;
|
||||
|
||||
int poptBitsAdd(poptBits bits, const char * s);
|
||||
int poptBitsChk(poptBits bits, const char * s);
|
||||
int poptBitsClr(poptBits bits);
|
||||
int poptBitsDel(poptBits bits, const char * s);
|
||||
int poptBitsIntersect(poptBits * ap, const poptBits b);
|
||||
int poptBitsUnion(poptBits * ap, const poptBits b);
|
||||
int poptBitsArgs(poptContext con, poptBits * ap);
|
||||
|
||||
/**
|
||||
* Save a string into a bit set (experimental).
|
||||
* @retval *bits bit set (lazily malloc'd if NULL)
|
||||
* @param argInfo logical operation (see POPT_ARGFLAG_*)
|
||||
* @param s string to add to bit set
|
||||
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
|
||||
*/
|
||||
int poptSaveBits(poptBits * bitsp, unsigned int argInfo,
|
||||
const char * s);
|
||||
|
||||
|
||||
/*@=type@*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/poptconfig.c
|
||||
* @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -8,54 +8,300 @@
|
||||
|
||||
#include "system.h"
|
||||
#include "poptint.h"
|
||||
/*@access poptContext @*/
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
|
||||
static void configLine(poptContext con, char * line)
|
||||
/*@modifies con @*/
|
||||
#if defined(HAVE_FNMATCH_H)
|
||||
#include <fnmatch.h>
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_GLOB_H)
|
||||
#include <glob.h>
|
||||
|
||||
#if !defined(HAVE_GLOB_PATTERN_P)
|
||||
/* Return nonzero if PATTERN contains any metacharacters.
|
||||
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
|
||||
static int
|
||||
glob_pattern_p (const char * pattern, int quote)
|
||||
{
|
||||
size_t nameLength;
|
||||
const char * p;
|
||||
int open = 0;
|
||||
|
||||
for (p = pattern; *p != '\0'; ++p)
|
||||
switch (*p) {
|
||||
case '?':
|
||||
case '*':
|
||||
return 1;
|
||||
break;
|
||||
case '\\':
|
||||
if (quote && p[1] != '\0')
|
||||
++p;
|
||||
break;
|
||||
case '[':
|
||||
open = 1;
|
||||
break;
|
||||
case ']':
|
||||
if (open)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* !defined(__GLIBC__) */
|
||||
|
||||
static int poptGlobFlags = 0;
|
||||
|
||||
static int poptGlob_error(UNUSED(const char * epath),
|
||||
UNUSED(int eerrno))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* HAVE_GLOB_H */
|
||||
|
||||
/**
|
||||
* Return path(s) from a glob pattern.
|
||||
* @param con context
|
||||
* @param pattern glob pattern
|
||||
* @retval *acp no. of paths
|
||||
* @retval *avp array of paths
|
||||
* @return 0 on success
|
||||
*/
|
||||
static int poptGlob(UNUSED(poptContext con), const char * pattern,
|
||||
int * acp, const char *** avp)
|
||||
{
|
||||
const char * pat = pattern;
|
||||
int rc = 0; /* assume success */
|
||||
|
||||
#if defined(HAVE_GLOB_H)
|
||||
if (glob_pattern_p(pat, 0)) {
|
||||
glob_t _g, *pglob = &_g;
|
||||
|
||||
if (!(rc = glob(pat, poptGlobFlags, poptGlob_error, pglob))) {
|
||||
if (acp) {
|
||||
*acp = (int) pglob->gl_pathc;
|
||||
pglob->gl_pathc = 0;
|
||||
}
|
||||
if (avp) {
|
||||
*avp = (const char **) pglob->gl_pathv;
|
||||
pglob->gl_pathv = NULL;
|
||||
}
|
||||
globfree(pglob);
|
||||
} else if (rc == GLOB_NOMATCH) {
|
||||
*avp = NULL;
|
||||
*acp = 0;
|
||||
rc = 0;
|
||||
} else
|
||||
rc = POPT_ERROR_ERRNO;
|
||||
} else
|
||||
#endif /* HAVE_GLOB_H */
|
||||
{
|
||||
if (acp)
|
||||
*acp = 1;
|
||||
if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
|
||||
(*avp)[0] = xstrdup(pat);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int poptSaneFile(const char * fn)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
|
||||
return 0;
|
||||
if (stat(fn, &sb) == -1)
|
||||
return 0;
|
||||
if (!S_ISREG(sb.st_mode))
|
||||
return 0;
|
||||
if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
|
||||
{
|
||||
int fdno;
|
||||
char * b = NULL;
|
||||
off_t nb = 0;
|
||||
char * s, * t, * se;
|
||||
int rc = POPT_ERROR_ERRNO; /* assume failure */
|
||||
|
||||
fdno = open(fn, O_RDONLY);
|
||||
if (fdno < 0)
|
||||
goto exit;
|
||||
|
||||
if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
|
||||
|| (uintmax_t)nb >= SIZE_MAX
|
||||
|| lseek(fdno, 0, SEEK_SET) == (off_t)-1
|
||||
|| (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
|
||||
|| read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
|
||||
{
|
||||
int oerrno = errno;
|
||||
(void) close(fdno);
|
||||
if (nb != (off_t)-1 && (uintmax_t)nb >= SIZE_MAX)
|
||||
errno = -EOVERFLOW;
|
||||
else
|
||||
errno = oerrno;
|
||||
goto exit;
|
||||
}
|
||||
if (close(fdno) == -1)
|
||||
goto exit;
|
||||
if (b == NULL) {
|
||||
rc = POPT_ERROR_MALLOC;
|
||||
goto exit;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
/* Trim out escaped newlines. */
|
||||
if (flags & POPT_READFILE_TRIMNEWLINES)
|
||||
{
|
||||
for (t = b, s = b, se = b + nb; *s && s < se; s++) {
|
||||
switch (*s) {
|
||||
case '\\':
|
||||
if (s[1] == '\n') {
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
/* fallthrough */
|
||||
default:
|
||||
*t++ = *s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*t++ = '\0';
|
||||
nb = (off_t)(t - b);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (rc != 0) {
|
||||
if (b)
|
||||
free(b);
|
||||
b = NULL;
|
||||
nb = 0;
|
||||
}
|
||||
if (bp)
|
||||
*bp = b;
|
||||
else if (b)
|
||||
free(b);
|
||||
if (nbp)
|
||||
*nbp = (size_t)nb;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for application match.
|
||||
* @param con context
|
||||
* @param s config application name
|
||||
* return 0 if config application matches
|
||||
*/
|
||||
static int configAppMatch(poptContext con, const char * s)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
if (con->appName == NULL) /* XXX can't happen. */
|
||||
return rc;
|
||||
|
||||
#if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
|
||||
if (glob_pattern_p(s, 1)) {
|
||||
static int flags = FNM_PATHNAME | FNM_PERIOD;
|
||||
#ifdef FNM_EXTMATCH
|
||||
flags |= FNM_EXTMATCH;
|
||||
#endif
|
||||
rc = fnmatch(s, con->appName, flags);
|
||||
} else
|
||||
#endif
|
||||
rc = strcmp(s, con->appName);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int poptConfigLine(poptContext con, char * line)
|
||||
{
|
||||
char *b = NULL;
|
||||
size_t nb = 0;
|
||||
char * se = line;
|
||||
const char * appName;
|
||||
const char * entryType;
|
||||
const char * opt;
|
||||
poptItem item = (poptItem) alloca(sizeof(*item));
|
||||
struct poptItem_s item_buf;
|
||||
poptItem item = &item_buf;
|
||||
int i, j;
|
||||
int rc = POPT_ERROR_BADCONFIG;
|
||||
|
||||
if (con->appName == NULL)
|
||||
return;
|
||||
nameLength = strlen(con->appName);
|
||||
goto exit;
|
||||
|
||||
/*@-boundswrite@*/
|
||||
memset(item, 0, sizeof(*item));
|
||||
|
||||
if (strncmp(line, con->appName, nameLength)) return;
|
||||
appName = se;
|
||||
while (*se != '\0' && !_isspaceptr(se)) se++;
|
||||
if (*se == '\0')
|
||||
goto exit;
|
||||
else
|
||||
*se++ = '\0';
|
||||
|
||||
line += nameLength;
|
||||
if (*line == '\0' || !isSpace(line)) return;
|
||||
if (configAppMatch(con, appName)) goto exit;
|
||||
|
||||
while (*line != '\0' && isSpace(line)) line++;
|
||||
entryType = line;
|
||||
while (*line == '\0' || !isSpace(line)) line++;
|
||||
*line++ = '\0';
|
||||
while (*se != '\0' && _isspaceptr(se)) se++;
|
||||
entryType = se;
|
||||
while (*se != '\0' && !_isspaceptr(se)) se++;
|
||||
if (*se != '\0') *se++ = '\0';
|
||||
|
||||
while (*line != '\0' && isSpace(line)) line++;
|
||||
if (*line == '\0') return;
|
||||
opt = line;
|
||||
while (*line == '\0' || !isSpace(line)) line++;
|
||||
*line++ = '\0';
|
||||
while (*se != '\0' && _isspaceptr(se)) se++;
|
||||
if (*se == '\0') goto exit;
|
||||
opt = se;
|
||||
while (*se != '\0' && !_isspaceptr(se)) se++;
|
||||
if (opt[0] == '-' && *se == '\0') goto exit;
|
||||
if (*se != '\0') *se++ = '\0';
|
||||
|
||||
while (*line != '\0' && isSpace(line)) line++;
|
||||
if (*line == '\0') return;
|
||||
while (*se != '\0' && _isspaceptr(se)) se++;
|
||||
if (opt[0] == '-' && *se == '\0') goto exit;
|
||||
|
||||
/*@-temptrans@*/ /* FIX: line alias is saved */
|
||||
if (opt[0] == '-' && opt[1] == '-')
|
||||
item->option.longName = opt + 2;
|
||||
else if (opt[0] == '-' && opt[2] == '\0')
|
||||
item->option.shortName = opt[1];
|
||||
/*@=temptrans@*/
|
||||
else {
|
||||
const char * fn = opt;
|
||||
|
||||
if (poptParseArgvString(line, &item->argc, &item->argv)) return;
|
||||
/* XXX handle globs and directories in fn? */
|
||||
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
|
||||
goto exit;
|
||||
if (b == NULL || nb == 0)
|
||||
goto exit;
|
||||
|
||||
/* Append remaining text to the interpolated file option text. */
|
||||
if (*se != '\0') {
|
||||
size_t nse = strlen(se) + 1;
|
||||
if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
|
||||
goto exit;
|
||||
(void) stpcpy( stpcpy(&b[nb-1], " "), se);
|
||||
nb += nse;
|
||||
}
|
||||
se = b;
|
||||
|
||||
/* Use the basename of the path as the long option name. */
|
||||
{ const char * longName = strrchr(fn, '/');
|
||||
if (longName != NULL)
|
||||
longName++;
|
||||
else
|
||||
longName = fn;
|
||||
if (longName == NULL) /* XXX can't happen. */
|
||||
goto exit;
|
||||
/* Single character basenames are treated as short options. */
|
||||
if (longName[1] != '\0')
|
||||
item->option.longName = longName;
|
||||
else
|
||||
item->option.shortName = longName[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
|
||||
|
||||
/*@-modobserver@*/
|
||||
item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
|
||||
for (i = 0, j = 0; i < item->argc; i++, j++) {
|
||||
const char * f;
|
||||
@@ -81,103 +327,183 @@ static void configLine(poptContext con, char * line)
|
||||
item->argv[j] = NULL;
|
||||
item->argc = j;
|
||||
}
|
||||
/*@=modobserver@*/
|
||||
/*@=boundswrite@*/
|
||||
|
||||
/*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
|
||||
if (!strcmp(entryType, "alias"))
|
||||
(void) poptAddItem(con, item, 0);
|
||||
rc = poptAddItem(con, item, 0);
|
||||
else if (!strcmp(entryType, "exec"))
|
||||
(void) poptAddItem(con, item, 1);
|
||||
/*@=nullstate@*/
|
||||
rc = poptAddItem(con, item, 1);
|
||||
exit:
|
||||
rc = 0; /* XXX for now, always return success */
|
||||
if (b)
|
||||
free(b);
|
||||
return rc;
|
||||
}
|
||||
/*@=compmempass@*/
|
||||
|
||||
int poptReadConfigFile(poptContext con, const char * fn)
|
||||
{
|
||||
const char * file, * chptr, * end;
|
||||
char * buf;
|
||||
/*@dependent@*/ char * dst;
|
||||
int fd, rc;
|
||||
off_t fileLength;
|
||||
|
||||
fd = open(fn, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);
|
||||
|
||||
fileLength = lseek(fd, 0, SEEK_END);
|
||||
if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
|
||||
rc = errno;
|
||||
(void) close(fd);
|
||||
errno = rc;
|
||||
return POPT_ERROR_ERRNO;
|
||||
}
|
||||
|
||||
file = alloca(fileLength + 1);
|
||||
if (read(fd, (char *)file, fileLength) != fileLength) {
|
||||
rc = errno;
|
||||
(void) close(fd);
|
||||
errno = rc;
|
||||
return POPT_ERROR_ERRNO;
|
||||
}
|
||||
if (close(fd) == -1)
|
||||
return POPT_ERROR_ERRNO;
|
||||
|
||||
/*@-boundswrite@*/
|
||||
dst = buf = alloca(fileLength + 1);
|
||||
|
||||
chptr = file;
|
||||
end = (file + fileLength);
|
||||
/*@-infloops@*/ /* LCL: can't detect chptr++ */
|
||||
while (chptr < end) {
|
||||
switch (*chptr) {
|
||||
case '\n':
|
||||
*dst = '\0';
|
||||
dst = buf;
|
||||
while (*dst && isSpace(dst)) dst++;
|
||||
if (*dst && *dst != '#')
|
||||
configLine(con, dst);
|
||||
chptr++;
|
||||
/*@switchbreak@*/ break;
|
||||
case '\\':
|
||||
*dst++ = *chptr++;
|
||||
if (chptr < end) {
|
||||
if (*chptr == '\n')
|
||||
dst--, chptr++;
|
||||
/* \ at the end of a line does not insert a \n */
|
||||
else
|
||||
*dst++ = *chptr++;
|
||||
}
|
||||
/*@switchbreak@*/ break;
|
||||
default:
|
||||
*dst++ = *chptr++;
|
||||
/*@switchbreak@*/ break;
|
||||
}
|
||||
}
|
||||
/*@=infloops@*/
|
||||
/*@=boundswrite@*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
|
||||
{
|
||||
char * fn, * home;
|
||||
char * b = NULL, *be;
|
||||
size_t nb = 0;
|
||||
const char *se;
|
||||
char *t = NULL, *te;
|
||||
int rc;
|
||||
|
||||
if (con->appName == NULL) return 0;
|
||||
|
||||
rc = poptReadConfigFile(con, "/etc/popt");
|
||||
if (rc) return rc;
|
||||
|
||||
if ((home = getenv("HOME"))) {
|
||||
size_t bufsize = strlen(home) + 20;
|
||||
fn = alloca(bufsize);
|
||||
if (fn == NULL) return 0;
|
||||
snprintf(fn, bufsize, "%s/.popt", home);
|
||||
rc = poptReadConfigFile(con, fn);
|
||||
if (rc) return rc;
|
||||
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
|
||||
return (errno == ENOENT ? 0 : rc);
|
||||
if (b == NULL || nb == 0) {
|
||||
rc = POPT_ERROR_BADCONFIG;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if ((t = malloc(nb + 1)) == NULL)
|
||||
goto exit;
|
||||
te = t;
|
||||
|
||||
be = (b + nb);
|
||||
for (se = b; se < be; se++) {
|
||||
switch (*se) {
|
||||
case '\n':
|
||||
*te = '\0';
|
||||
te = t;
|
||||
while (*te && _isspaceptr(te)) te++;
|
||||
if (*te && *te != '#')
|
||||
if ((rc = poptConfigLine(con, te)) != 0)
|
||||
goto exit;
|
||||
break;
|
||||
case '\\':
|
||||
*te = *se++;
|
||||
/* \ at the end of a line does not insert a \n */
|
||||
if (se < be && *se != '\n') {
|
||||
te++;
|
||||
*te++ = *se;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*te++ = *se;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
exit:
|
||||
free(t);
|
||||
if (b)
|
||||
free(b);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int poptReadConfigFiles(poptContext con, const char * paths)
|
||||
{
|
||||
char * buf = (paths ? xstrdup(paths) : NULL);
|
||||
const char * p;
|
||||
char * pe;
|
||||
int rc = 0; /* assume success */
|
||||
|
||||
for (p = buf; p != NULL && *p != '\0'; p = pe) {
|
||||
const char ** av = NULL;
|
||||
int ac = 0;
|
||||
int i;
|
||||
int xx;
|
||||
|
||||
/* locate start of next path element */
|
||||
pe = strchr(p, ':');
|
||||
if (pe != NULL && *pe == ':')
|
||||
*pe++ = '\0';
|
||||
else
|
||||
pe = (char *) (p + strlen(p));
|
||||
|
||||
xx = poptGlob(con, p, &ac, &av);
|
||||
|
||||
/* work-off each resulting file from the path element */
|
||||
for (i = 0; i < ac; i++) {
|
||||
const char * fn = av[i];
|
||||
if (!poptSaneFile(fn))
|
||||
continue;
|
||||
xx = poptReadConfigFile(con, fn);
|
||||
if (xx && rc == 0)
|
||||
rc = xx;
|
||||
free((void *)av[i]);
|
||||
av[i] = NULL;
|
||||
}
|
||||
free(av);
|
||||
av = NULL;
|
||||
}
|
||||
|
||||
if (buf)
|
||||
free(buf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int poptReadDefaultConfig(poptContext con, UNUSED(int useEnv))
|
||||
{
|
||||
char * home;
|
||||
struct stat sb;
|
||||
int rc = 0; /* assume success */
|
||||
|
||||
if (con->appName == NULL) goto exit;
|
||||
|
||||
rc = poptReadConfigFile(con, POPT_SYSCONFDIR "/popt");
|
||||
if (rc) goto exit;
|
||||
|
||||
#if defined(HAVE_GLOB_H)
|
||||
if (!stat(POPT_SYSCONFDIR "/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
|
||||
const char ** av = NULL;
|
||||
int ac = 0;
|
||||
int i;
|
||||
|
||||
if ((rc = poptGlob(con, POPT_SYSCONFDIR "/popt.d/*", &ac, &av)) == 0) {
|
||||
for (i = 0; rc == 0 && i < ac; i++) {
|
||||
const char * fn = av[i];
|
||||
if (!poptSaneFile(fn))
|
||||
continue;
|
||||
rc = poptReadConfigFile(con, fn);
|
||||
free((void *)av[i]);
|
||||
av[i] = NULL;
|
||||
}
|
||||
free(av);
|
||||
av = NULL;
|
||||
}
|
||||
}
|
||||
if (rc) goto exit;
|
||||
#endif
|
||||
|
||||
if ((home = getenv("HOME"))) {
|
||||
char * fn = malloc(strlen(home) + 20);
|
||||
if (fn != NULL) {
|
||||
(void) stpcpy(stpcpy(fn, home), "/.popt");
|
||||
rc = poptReadConfigFile(con, fn);
|
||||
free(fn);
|
||||
} else
|
||||
rc = POPT_ERROR_ERRNO;
|
||||
if (rc) goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
poptContext
|
||||
poptFini(poptContext con)
|
||||
{
|
||||
return poptFreeContext(con);
|
||||
}
|
||||
|
||||
poptContext
|
||||
poptInit(int argc, const char ** argv,
|
||||
const struct poptOption * options, const char * configPaths)
|
||||
{
|
||||
poptContext con = NULL;
|
||||
const char * argv0;
|
||||
|
||||
if (argv == NULL || argv[0] == NULL || options == NULL)
|
||||
return con;
|
||||
|
||||
if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
|
||||
else argv0 = argv[0];
|
||||
|
||||
con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
|
||||
if (con != NULL&& poptReadConfigFiles(con, configPaths))
|
||||
con = poptFini(con);
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
774
popt/popthelp.c
774
popt/popthelp.c
File diff suppressed because it is too large
Load Diff
194
popt/poptint.c
Normal file
194
popt/poptint.c
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "system.h"
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_LANGINFO_H
|
||||
#include <langinfo.h>
|
||||
#endif
|
||||
#include "poptint.h"
|
||||
|
||||
/* Any pair of 32 bit hashes can be used. lookup3.c generates pairs, will do. */
|
||||
#define _JLU3_jlu32lpair 1
|
||||
#define jlu32lpair poptJlu32lpair
|
||||
#include "lookup3.c"
|
||||
|
||||
const char *
|
||||
POPT_prev_char (const char *str)
|
||||
{
|
||||
const char *p = str;
|
||||
|
||||
while (1) {
|
||||
p--;
|
||||
if (((unsigned)*p & 0xc0) != (unsigned)0x80)
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
POPT_next_char (const char *str)
|
||||
{
|
||||
const char *p = str;
|
||||
|
||||
while (*p != '\0') {
|
||||
p++;
|
||||
if (((unsigned)*p & 0xc0) != (unsigned)0x80)
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#if !defined(POPT_fprintf) /* XXX lose all the goop ... */
|
||||
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
|
||||
/*
|
||||
* Rebind a "UTF-8" codeset for popt's internal use.
|
||||
*/
|
||||
char *
|
||||
POPT_dgettext(const char * dom, const char * str)
|
||||
{
|
||||
char * codeset = NULL;
|
||||
char * retval = NULL;
|
||||
|
||||
if (!dom)
|
||||
dom = textdomain(NULL);
|
||||
codeset = bind_textdomain_codeset(dom, NULL);
|
||||
bind_textdomain_codeset(dom, "UTF-8");
|
||||
retval = dgettext(dom, str);
|
||||
bind_textdomain_codeset(dom, codeset);
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ICONV
|
||||
/**
|
||||
* Return malloc'd string converted from UTF-8 to current locale.
|
||||
* @param istr input string (UTF-8 encoding assumed)
|
||||
* @return localized string
|
||||
*/
|
||||
static char *
|
||||
strdup_locale_from_utf8 (char * istr)
|
||||
{
|
||||
char * codeset = NULL;
|
||||
char * ostr = NULL;
|
||||
iconv_t cd;
|
||||
|
||||
if (istr == NULL)
|
||||
return NULL;
|
||||
|
||||
#ifdef HAVE_LANGINFO_H
|
||||
codeset = nl_langinfo ((nl_item)CODESET);
|
||||
#endif
|
||||
|
||||
if (codeset != NULL && strcmp(codeset, "UTF-8") != 0
|
||||
&& (cd = iconv_open(codeset, "UTF-8")) != (iconv_t)-1)
|
||||
{
|
||||
char * shift_pin = NULL;
|
||||
size_t db = strlen(istr);
|
||||
char * dstr = malloc((db + 1) * sizeof(*dstr));
|
||||
char * dstr_tmp;
|
||||
char * pin = istr;
|
||||
char * pout = dstr;
|
||||
size_t ib = db;
|
||||
size_t ob = db;
|
||||
size_t err;
|
||||
|
||||
if (dstr == NULL) {
|
||||
(void) iconv_close(cd);
|
||||
return NULL;
|
||||
}
|
||||
err = iconv(cd, NULL, NULL, NULL, NULL);
|
||||
while (1) {
|
||||
*pout = '\0';
|
||||
err = iconv(cd, &pin, &ib, &pout, &ob);
|
||||
if (err != (size_t)-1) {
|
||||
if (shift_pin == NULL) {
|
||||
shift_pin = pin;
|
||||
pin = NULL;
|
||||
ib = 0;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
switch (errno) {
|
||||
case E2BIG:
|
||||
{ size_t used = (size_t)(pout - dstr);
|
||||
db *= 2;
|
||||
dstr_tmp = realloc(dstr, (db + 1) * sizeof(*dstr));
|
||||
if (dstr_tmp == NULL) {
|
||||
free(dstr);
|
||||
(void) iconv_close(cd);
|
||||
return NULL;
|
||||
}
|
||||
dstr = dstr_tmp;
|
||||
pout = dstr + used;
|
||||
ob = db - used;
|
||||
continue;
|
||||
} break;
|
||||
case EINVAL:
|
||||
case EILSEQ:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
(void) iconv_close(cd);
|
||||
*pout = '\0';
|
||||
ostr = xstrdup(dstr);
|
||||
free(dstr);
|
||||
} else
|
||||
ostr = xstrdup(istr);
|
||||
|
||||
return ostr;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
POPT_fprintf (FILE * stream, const char * format, ...)
|
||||
{
|
||||
char * b = NULL, * ob = NULL;
|
||||
int rc;
|
||||
va_list ap;
|
||||
|
||||
#if defined(HAVE_VASPRINTF)
|
||||
va_start(ap, format);
|
||||
if ((rc = vasprintf(&b, format, ap)) < 0)
|
||||
b = NULL;
|
||||
va_end(ap);
|
||||
#else
|
||||
size_t nb = (size_t)1;
|
||||
|
||||
/* HACK: add +1 to the realloc no. of bytes "just in case". */
|
||||
/* XXX Likely unneeded, the issues wrto vsnprintf(3) return b0rkage have
|
||||
* to do with whether the final '\0' is counted (or not). The code
|
||||
* below already adds +1 for the (possibly already counted) trailing NUL.
|
||||
*/
|
||||
while ((b = realloc(b, nb+1)) != NULL) {
|
||||
va_start(ap, format);
|
||||
rc = vsnprintf(b, nb, format, ap);
|
||||
va_end(ap);
|
||||
if (rc > -1) { /* glibc 2.1 */
|
||||
if ((size_t)rc < nb)
|
||||
break;
|
||||
nb = (size_t)(rc + 1); /* precise buffer length known */
|
||||
} else /* glibc 2.0 */
|
||||
nb += (nb < (size_t)100 ? (size_t)100 : nb);
|
||||
ob = b;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = 0;
|
||||
if (b != NULL) {
|
||||
#ifdef HAVE_ICONV
|
||||
ob = strdup_locale_from_utf8(b);
|
||||
if (ob != NULL) {
|
||||
rc = fprintf(stream, "%s", ob);
|
||||
free(ob);
|
||||
} else
|
||||
#endif
|
||||
rc = fprintf(stream, "%s", b);
|
||||
free (b);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* !defined(POPT_fprintf) */
|
||||
118
popt/poptint.h
118
popt/poptint.h
@@ -1,5 +1,5 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/poptint.h
|
||||
* @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -9,108 +9,145 @@
|
||||
#ifndef H_POPTINT
|
||||
#define H_POPTINT
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
|
||||
* @param p memory to free
|
||||
* @retval NULL always
|
||||
*/
|
||||
/*@unused@*/ static inline /*@null@*/ void *
|
||||
_free(/*@only@*/ /*@null@*/ const void * p)
|
||||
/*@modifies p @*/
|
||||
static inline void *
|
||||
_free(const void * p)
|
||||
{
|
||||
if (p != NULL) free((void *)p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
isSpace(const char *ptr)
|
||||
{
|
||||
return isspace(*(unsigned char *)ptr);
|
||||
}
|
||||
|
||||
/* Bit mask macros. */
|
||||
/*@-exporttype -redef @*/
|
||||
typedef unsigned int __pbm_bits;
|
||||
/*@=exporttype =redef @*/
|
||||
#define __PBM_NBITS (8 * sizeof (__pbm_bits))
|
||||
#define __PBM_IX(d) ((d) / __PBM_NBITS)
|
||||
#define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
|
||||
/*@-exporttype -redef @*/
|
||||
typedef struct {
|
||||
__pbm_bits bits[1];
|
||||
} pbm_set;
|
||||
/*@=exporttype =redef @*/
|
||||
#define __PBM_BITS(set) ((set)->bits)
|
||||
|
||||
#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
|
||||
#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(pbm_set))
|
||||
#define PBM_FREE(s) _free(s);
|
||||
#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
|
||||
#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
|
||||
#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
|
||||
|
||||
extern void poptJlu32lpair(const void *key, size_t size,
|
||||
uint32_t *pc, uint32_t *pb);
|
||||
|
||||
/** \ingroup popt
|
||||
* Typedef's for string and array of strings.
|
||||
*/
|
||||
typedef const char * poptString;
|
||||
typedef poptString * poptArgv;
|
||||
|
||||
/** \ingroup popt
|
||||
* A union to simplify opt->arg access without casting.
|
||||
*/
|
||||
typedef union poptArg_u {
|
||||
void * ptr;
|
||||
int * intp;
|
||||
short * shortp;
|
||||
long * longp;
|
||||
long long * longlongp;
|
||||
float * floatp;
|
||||
double * doublep;
|
||||
const char ** argv;
|
||||
poptCallbackType cb;
|
||||
poptOption opt;
|
||||
} poptArg;
|
||||
|
||||
extern unsigned int _poptArgMask;
|
||||
extern unsigned int _poptGroupMask;
|
||||
|
||||
#define poptArgType(_opt) ((_opt)->argInfo & _poptArgMask)
|
||||
#define poptGroup(_opt) ((_opt)->argInfo & _poptGroupMask)
|
||||
|
||||
#define F_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_ARGFLAG_##_FLAG)
|
||||
#define LF_ISSET(_FLAG) (argInfo & POPT_ARGFLAG_##_FLAG)
|
||||
#define CBF_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_CBFLAG_##_FLAG)
|
||||
|
||||
/* XXX sick hack to preserve pretense of a popt-1.x ABI. */
|
||||
#define poptSubstituteHelpI18N(opt) \
|
||||
{ if ((opt) == poptHelpOptions) (opt) = poptHelpOptionsI18N; }
|
||||
|
||||
struct optionStackEntry {
|
||||
int argc;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char ** argv;
|
||||
/*@only@*/ /*@null@*/
|
||||
poptArgv argv;
|
||||
pbm_set * argb;
|
||||
int next;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char * nextArg;
|
||||
/*@observer@*/ /*@null@*/
|
||||
char * nextArg;
|
||||
const char * nextCharArg;
|
||||
/*@dependent@*/ /*@null@*/
|
||||
poptItem currAlias;
|
||||
int stuffed;
|
||||
};
|
||||
|
||||
struct poptContext_s {
|
||||
struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
|
||||
/*@dependent@*/
|
||||
struct optionStackEntry * os;
|
||||
/*@owned@*/ /*@null@*/
|
||||
const char ** leftovers;
|
||||
poptArgv leftovers;
|
||||
int numLeftovers;
|
||||
int allocLeftovers;
|
||||
int nextLeftover;
|
||||
/*@keep@*/
|
||||
const struct poptOption * options;
|
||||
int restLeftover;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char * appName;
|
||||
/*@only@*/ /*@null@*/
|
||||
poptItem aliases;
|
||||
int numAliases;
|
||||
int flags;
|
||||
/*@owned@*/ /*@null@*/
|
||||
unsigned int flags;
|
||||
poptItem execs;
|
||||
int numExecs;
|
||||
/*@only@*/ /*@null@*/
|
||||
const char ** finalArgv;
|
||||
char * execFail;
|
||||
poptArgv finalArgv;
|
||||
int finalArgvCount;
|
||||
int finalArgvAlloced;
|
||||
/*@dependent@*/ /*@null@*/
|
||||
int (*maincall) (int argc, const char **argv);
|
||||
poptItem doExec;
|
||||
/*@only@*/
|
||||
const char * execPath;
|
||||
int execAbsolute;
|
||||
/*@only@*/ /*@relnull@*/
|
||||
const char * otherHelp;
|
||||
/*@null@*/
|
||||
pbm_set * arg_strip;
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBINTL_H
|
||||
#if defined(POPT_fprintf)
|
||||
#define POPT_dgettext dgettext
|
||||
#else
|
||||
#ifdef HAVE_ICONV
|
||||
#include <iconv.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DCGETTEXT)
|
||||
char *POPT_dgettext(const char * dom, const char * str);
|
||||
#endif
|
||||
|
||||
FORMAT(printf, 2, 3)
|
||||
int POPT_fprintf (FILE* stream, const char *format, ...);
|
||||
#endif /* !defined(POPT_fprintf) */
|
||||
|
||||
const char *POPT_prev_char (const char *str);
|
||||
const char *POPT_next_char (const char *str);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H)
|
||||
#include <libintl.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
|
||||
#define _(foo) gettext(foo)
|
||||
#else
|
||||
#define _(foo) foo
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
|
||||
#define D_(dom, str) dgettext(dom, str)
|
||||
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
|
||||
#define D_(dom, str) POPT_dgettext(dom, str)
|
||||
#define POPT_(foo) D_("popt", foo)
|
||||
#else
|
||||
#define D_(dom, str) str
|
||||
@@ -119,4 +156,3 @@ struct poptContext_s {
|
||||
|
||||
#define N_(foo) foo
|
||||
|
||||
#endif
|
||||
|
||||
100
popt/poptparse.c
100
popt/poptparse.c
@@ -1,5 +1,5 @@
|
||||
/** \ingroup popt
|
||||
* \file popt/poptparse.c
|
||||
* @file
|
||||
*/
|
||||
|
||||
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
|
||||
@@ -8,11 +8,8 @@
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include "poptint.h"
|
||||
|
||||
#define POPT_ARGV_ARRAY_GROW_DELTA 5
|
||||
|
||||
/*@-boundswrite@*/
|
||||
int poptDupArgv(int argc, const char **argv,
|
||||
int * argcPtr, const char *** argvPtr)
|
||||
{
|
||||
@@ -34,13 +31,13 @@ int poptDupArgv(int argc, const char **argv,
|
||||
return POPT_ERROR_MALLOC;
|
||||
argv2 = (void *) dst;
|
||||
dst += (argc + 1) * sizeof(*argv);
|
||||
*dst = '\0';
|
||||
|
||||
/*@-branchstate@*/
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv2[i] = dst;
|
||||
dst += strlcpy(dst, argv[i], nb) + 1;
|
||||
dst = stpcpy(dst, argv[i]);
|
||||
dst++; /* trailing NUL */
|
||||
}
|
||||
/*@=branchstate@*/
|
||||
argv2[argc] = NULL;
|
||||
|
||||
if (argvPtr) {
|
||||
@@ -53,21 +50,25 @@ int poptDupArgv(int argc, const char **argv,
|
||||
*argcPtr = argc;
|
||||
return 0;
|
||||
}
|
||||
/*@=boundswrite@*/
|
||||
|
||||
/*@-bounds@*/
|
||||
int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
{
|
||||
const char * src;
|
||||
char quote = '\0';
|
||||
int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
|
||||
const char ** argv = malloc(sizeof(*argv) * argvAlloced);
|
||||
const char ** argv_tmp;
|
||||
int argc = 0;
|
||||
int buflen = strlen(s) + 1;
|
||||
char * buf = memset(alloca(buflen), 0, buflen);
|
||||
size_t buflen = strlen(s) + 1;
|
||||
char * buf, * bufOrig = NULL;
|
||||
int rc = POPT_ERROR_MALLOC;
|
||||
|
||||
if (argv == NULL) return rc;
|
||||
buf = bufOrig = calloc((size_t)1, buflen);
|
||||
if (buf == NULL) {
|
||||
free(argv);
|
||||
return rc;
|
||||
}
|
||||
argv[argc] = buf;
|
||||
|
||||
for (src = s; *src != '\0'; src++) {
|
||||
@@ -83,13 +84,14 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
if (*src != quote) *buf++ = '\\';
|
||||
}
|
||||
*buf++ = *src;
|
||||
} else if (isSpace(src)) {
|
||||
} else if (_isspaceptr(src)) {
|
||||
if (*argv[argc] != '\0') {
|
||||
buf++, argc++;
|
||||
if (argc == argvAlloced) {
|
||||
argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
|
||||
argv = realloc(argv, sizeof(*argv) * argvAlloced);
|
||||
if (argv == NULL) goto exit;
|
||||
argv_tmp = realloc(argv, sizeof(*argv) * argvAlloced);
|
||||
if (argv_tmp == NULL) goto exit;
|
||||
argv = argv_tmp;
|
||||
}
|
||||
argv[argc] = buf;
|
||||
}
|
||||
@@ -97,17 +99,17 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
case '"':
|
||||
case '\'':
|
||||
quote = *src;
|
||||
/*@switchbreak@*/ break;
|
||||
break;
|
||||
case '\\':
|
||||
src++;
|
||||
if (!*src) {
|
||||
rc = POPT_ERROR_BADQUOTE;
|
||||
goto exit;
|
||||
}
|
||||
/*@fallthrough@*/
|
||||
/* fallthrough */
|
||||
default:
|
||||
*buf++ = *src;
|
||||
/*@switchbreak@*/ break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,29 +120,30 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
|
||||
rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
|
||||
|
||||
exit:
|
||||
if (bufOrig) free(bufOrig);
|
||||
if (argv) free(argv);
|
||||
return rc;
|
||||
}
|
||||
/*@=bounds@*/
|
||||
|
||||
/* still in the dev stage.
|
||||
* return values, perhaps 1== file erro
|
||||
* return values, perhaps 1== file error
|
||||
* 2== line to long
|
||||
* 3== umm.... more?
|
||||
*/
|
||||
int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int flags))
|
||||
int poptConfigFileToString(FILE *fp, char ** argstrp,
|
||||
UNUSED(int flags))
|
||||
{
|
||||
char line[999];
|
||||
char * argstr;
|
||||
char * argstr_tmp;
|
||||
char * p;
|
||||
char * q;
|
||||
char * x;
|
||||
int t;
|
||||
int argvlen = 0;
|
||||
size_t t;
|
||||
size_t argvlen = 0;
|
||||
size_t maxlinelen = sizeof(line);
|
||||
size_t linelen;
|
||||
int maxargvlen = 480;
|
||||
int linenum = 0;
|
||||
size_t maxargvlen = (size_t)480;
|
||||
|
||||
*argstrp = NULL;
|
||||
|
||||
@@ -155,11 +158,10 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
|
||||
if (argstr == NULL) return POPT_ERROR_MALLOC;
|
||||
|
||||
while (fgets(line, (int)maxlinelen, fp) != NULL) {
|
||||
linenum++;
|
||||
p = line;
|
||||
|
||||
/* loop until first non-space char or EOL */
|
||||
while( *p != '\0' && isSpace(p) )
|
||||
while( *p != '\0' && _isspaceptr(p) )
|
||||
p++;
|
||||
|
||||
linelen = strlen(p);
|
||||
@@ -173,25 +175,29 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
|
||||
|
||||
q = p;
|
||||
|
||||
while (*q != '\0' && (!isSpace(q)) && *q != '=')
|
||||
while (*q != '\0' && (!_isspaceptr(q)) && *q != '=')
|
||||
q++;
|
||||
|
||||
if (isSpace(q)) {
|
||||
if (_isspaceptr(q)) {
|
||||
/* a space after the name, find next non space */
|
||||
*q++='\0';
|
||||
while( *q != '\0' && isSpace(q) ) q++;
|
||||
while( *q != '\0' && _isspaceptr(q) ) q++;
|
||||
}
|
||||
if (*q == '\0') {
|
||||
/* single command line option (ie, no name=val, just name) */
|
||||
q[-1] = '\0'; /* kill off newline from fgets() call */
|
||||
argvlen += (t = q - p) + (sizeof(" --")-1);
|
||||
argvlen += (t = (size_t)(q - p)) + (sizeof(" --")-1);
|
||||
if (argvlen >= maxargvlen) {
|
||||
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
|
||||
argstr = realloc(argstr, maxargvlen);
|
||||
if (argstr == NULL) return POPT_ERROR_MALLOC;
|
||||
argstr_tmp = realloc(argstr, maxargvlen);
|
||||
if (argstr_tmp == NULL) {
|
||||
free(argstr);
|
||||
return POPT_ERROR_MALLOC;
|
||||
}
|
||||
argstr = argstr_tmp;
|
||||
}
|
||||
strlcat(argstr, " --", maxargvlen);
|
||||
strlcat(argstr, p, maxargvlen);
|
||||
strcat(argstr, " --");
|
||||
strcat(argstr, p);
|
||||
continue;
|
||||
}
|
||||
if (*q != '=')
|
||||
@@ -201,29 +207,33 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
|
||||
*q++ = '\0';
|
||||
|
||||
/* find next non-space letter of value */
|
||||
while (*q != '\0' && isSpace(q))
|
||||
while (*q != '\0' && _isspaceptr(q))
|
||||
q++;
|
||||
if (*q == '\0')
|
||||
continue; /* XXX silently ignore missing value */
|
||||
|
||||
/* now, loop and strip all ending whitespace */
|
||||
x = p + linelen;
|
||||
while (isSpace(--x))
|
||||
*x = 0; /* null out last char if space (including fgets() NL) */
|
||||
while (_isspaceptr(--x))
|
||||
*x = '\0'; /* null out last char if space (including fgets() NL) */
|
||||
|
||||
/* rest of line accept */
|
||||
t = x - p;
|
||||
t = (size_t)(x - p);
|
||||
argvlen += t + (sizeof("' --='")-1);
|
||||
if (argvlen >= maxargvlen) {
|
||||
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
|
||||
argstr = realloc(argstr, maxargvlen);
|
||||
if (argstr == NULL) return POPT_ERROR_MALLOC;
|
||||
argstr_tmp = realloc(argstr, maxargvlen);
|
||||
if (argstr_tmp == NULL) {
|
||||
free(argstr);
|
||||
return POPT_ERROR_MALLOC;
|
||||
}
|
||||
argstr = argstr_tmp;
|
||||
}
|
||||
strlcat(argstr, " --", maxargvlen);
|
||||
strlcat(argstr, p, maxargvlen);
|
||||
strlcat(argstr, "=\"", maxargvlen);
|
||||
strlcat(argstr, q, maxargvlen);
|
||||
strlcat(argstr, "\"", maxargvlen);
|
||||
strcat(argstr, " --");
|
||||
strcat(argstr, p);
|
||||
strcat(argstr, "=\"");
|
||||
strcat(argstr, q);
|
||||
strcat(argstr, "\"");
|
||||
}
|
||||
|
||||
*argstrp = argstr;
|
||||
|
||||
154
popt/system.h
154
popt/system.h
@@ -1,134 +1,70 @@
|
||||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if defined (__GLIBC__) && defined(__LCLINT__)
|
||||
/*@-declundef@*/
|
||||
/*@unchecked@*/
|
||||
extern __const __int32_t *__ctype_tolower;
|
||||
/*@unchecked@*/
|
||||
extern __const __int32_t *__ctype_toupper;
|
||||
/*@=declundef@*/
|
||||
#endif
|
||||
|
||||
#ifdef __TANDEM
|
||||
# include <floss.h(floss_execvp,floss_read)>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
/* XXX isspace(3) has i18n encoding signedness issues on Solaris. */
|
||||
#define _isspaceptr(_chp) isspace((int)(*(unsigned const char *)(_chp)))
|
||||
|
||||
#if HAVE_MCHECK_H
|
||||
#ifdef HAVE_MCHECK_H
|
||||
#include <mcheck.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
# endif
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
void * xmalloc (size_t size);
|
||||
|
||||
#ifdef __NeXT
|
||||
/* access macros are not declared in non posix mode in unistd.h -
|
||||
don't try to use posix on NeXTstep 3.3 ! */
|
||||
#include <libc.h>
|
||||
#endif
|
||||
void * xcalloc (size_t nmemb, size_t size);
|
||||
|
||||
#if defined(__LCLINT__)
|
||||
/*@-declundef -incondefs @*/ /* LCL: missing annotation */
|
||||
/*@only@*/ /*@out@*/
|
||||
void * alloca (size_t __size)
|
||||
/*@ensures MaxSet(result) == (__size - 1) @*/
|
||||
/*@*/;
|
||||
/*@=declundef =incondefs @*/
|
||||
#endif
|
||||
void * xrealloc (void * ptr, size_t size);
|
||||
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifndef __GNUC__
|
||||
# if HAVE_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# ifdef _AIX
|
||||
#pragma alloca
|
||||
# else
|
||||
# ifdef HAVE_ALLOCA
|
||||
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
||||
char *alloca(size_t size);
|
||||
# endif
|
||||
# else
|
||||
# ifdef alloca
|
||||
# undef alloca
|
||||
# endif
|
||||
# define alloca(sz) malloc(sz) /* Kludge this for now */
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#elif !defined(alloca)
|
||||
#define alloca __builtin_alloca
|
||||
#endif
|
||||
char * xstrdup (const char *str);
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char *d, const char *s, size_t bufsize);
|
||||
#endif
|
||||
#if !defined(HAVE_STPCPY)
|
||||
/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */
|
||||
static inline char * stpcpy (char *dest, const char * src) {
|
||||
register char *d = dest;
|
||||
register const char *s = src;
|
||||
|
||||
#ifndef HAVE_STRLCAT
|
||||
size_t strlcat(char *d, const char *s, size_t bufsize);
|
||||
#endif
|
||||
|
||||
#if HAVE_MCHECK_H && defined(__GNUC__)
|
||||
static inline char *
|
||||
xstrdup(const char *s)
|
||||
{
|
||||
size_t memsize = strlen(s) + 1;
|
||||
char *ptr = malloc(memsize);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "virtual memory exhausted.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strlcpy(ptr, s, memsize);
|
||||
return ptr;
|
||||
do
|
||||
*d++ = *s;
|
||||
while (*s++ != '\0');
|
||||
return d - 1;
|
||||
}
|
||||
#else
|
||||
#define xstrdup(_str) strdup(_str)
|
||||
#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
|
||||
#endif
|
||||
|
||||
#if HAVE___SECURE_GETENV && !defined(__LCLINT__)
|
||||
/* Memory allocation via macro defs to get meaningful locations from mtrace() */
|
||||
#if defined(HAVE_MCHECK_H) && defined(__GNUC__)
|
||||
#define vmefail() (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
|
||||
#define xmalloc(_size) (malloc(_size) ? : vmefail())
|
||||
#define xcalloc(_nmemb, _size) (calloc((_nmemb), (_size)) ? : vmefail())
|
||||
#define xrealloc(_ptr, _size) (realloc((_ptr), (_size)) ? : vmefail())
|
||||
#define xstrdup(_str) (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
|
||||
#else
|
||||
#define xmalloc(_size) malloc(_size)
|
||||
#define xcalloc(_nmemb, _size) calloc((_nmemb), (_size))
|
||||
#define xrealloc(_ptr, _size) realloc((_ptr), (_size))
|
||||
#define xstrdup(_str) strdup(_str)
|
||||
#endif /* defined(HAVE_MCHECK_H) && defined(__GNUC__) */
|
||||
|
||||
#if defined(HAVE_SECURE_GETENV)
|
||||
#define getenv(_s) secure_getenv(_s)
|
||||
#elif defined(HAVE___SECURE_GETENV)
|
||||
#define getenv(_s) __secure_getenv(_s)
|
||||
#endif
|
||||
|
||||
#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
|
||||
#define snprintf rsync_snprintf
|
||||
int snprintf(char *str,size_t count,const char *fmt,...);
|
||||
#if !defined(__GNUC__) && !defined(__attribute__)
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#define UNUSED(x) x __attribute__((__unused__))
|
||||
|
||||
#define PACKAGE "rsync"
|
||||
#define FORMAT(a, b, c) __attribute__((__format__ (a, b, c)))
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
|
||||
#include "popt.h"
|
||||
|
||||
155
receiver.c
155
receiver.c
@@ -66,9 +66,11 @@ extern char sender_file_sum[MAX_DIGEST_LEN];
|
||||
extern struct file_list *cur_flist, *first_flist, *dir_flist;
|
||||
extern filter_rule_list daemon_filter_list;
|
||||
extern OFF_T preallocated_len;
|
||||
extern int fuzzy_basis;
|
||||
|
||||
extern struct name_num_item *xfer_sum_nni;
|
||||
extern int xfer_sum_len;
|
||||
extern int use_secure_symlinks;
|
||||
|
||||
static struct bitbag *delayed_bits = NULL;
|
||||
static int phase = 0, redoing = 0;
|
||||
@@ -81,6 +83,44 @@ static int updating_basis_or_equiv;
|
||||
#define MAX_UNIQUE_NUMBER 999999
|
||||
#define MAX_UNIQUE_LOOP 100
|
||||
|
||||
/* Open a basis/output path that may legitimately be an operator-trusted
|
||||
* ABSOLUTE path -- e.g. an absolute --partial-dir ("a directory reserved for
|
||||
* partial-dir work") or --backup-dir. secure_relative_open() deliberately
|
||||
* rejects an absolute relpath, so feeding it the whole absolute partialptr
|
||||
* (with a NULL basedir) returns EINVAL: the basis fd is then -1, no basis is
|
||||
* mapped, and receive_data() omits every matched block from the whole-file
|
||||
* verification checksum -> a spurious "failed verification" that strands the
|
||||
* (correct) data in the partial-dir forever.
|
||||
*
|
||||
* The operator's directory is trusted; only the leaf basename is peer-supplied.
|
||||
* So when basedir is NULL and relpath is absolute, split it into its directory
|
||||
* (trusted) and leaf and confine just the leaf -- exactly how secure_relative_
|
||||
* open already trusts an absolute basedir while O_NOFOLLOW-confining the leaf.
|
||||
* Anything else is a straight pass-through that preserves the strict contract. */
|
||||
static int secure_basis_open(const char *basedir, const char *relpath, int flags, mode_t mode)
|
||||
{
|
||||
if (!basedir && relpath && *relpath == '/') {
|
||||
const char *slash = strrchr(relpath, '/');
|
||||
const char *leaf = slash + 1;
|
||||
char dirbuf[MAXPATHLEN];
|
||||
const char *dir;
|
||||
if (slash == relpath)
|
||||
dir = "/";
|
||||
else {
|
||||
size_t dlen = slash - relpath;
|
||||
if (dlen >= sizeof dirbuf) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
memcpy(dirbuf, relpath, dlen);
|
||||
dirbuf[dlen] = '\0';
|
||||
dir = dirbuf;
|
||||
}
|
||||
return secure_relative_open(dir, leaf, flags, mode);
|
||||
}
|
||||
return secure_relative_open(basedir, relpath, flags, mode);
|
||||
}
|
||||
|
||||
/* get_tmpname() - create a tmp filename for a given filename
|
||||
*
|
||||
* If a tmpdir is defined, use that as the directory to put it in. Otherwise,
|
||||
@@ -213,7 +253,12 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
|
||||
* access to ensure that there is no race condition. They will be
|
||||
* correctly updated after the right owner and group info is set.
|
||||
* (Thanks to snabb@epipe.fi for pointing this out.) */
|
||||
fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
|
||||
/* When use_secure_symlinks is on (non-chroot daemon with munge_symlinks),
|
||||
* use secure_mkstemp to prevent symlink race attacks on parent directories. */
|
||||
if (use_secure_symlinks)
|
||||
fd = secure_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
|
||||
else
|
||||
fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
|
||||
|
||||
#if 0
|
||||
/* In most cases parent directories will already exist because their
|
||||
@@ -311,7 +356,12 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
}
|
||||
}
|
||||
|
||||
while ((i = recv_token(f_in, &data)) != 0) {
|
||||
while (1) {
|
||||
data = NULL;
|
||||
i = recv_token(f_in, &data);
|
||||
if (i == 0)
|
||||
break;
|
||||
|
||||
if (INFO_GTE(PROGRESS, 1))
|
||||
show_progress(offset, total_size);
|
||||
|
||||
@@ -319,6 +369,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
|
||||
|
||||
if (i > 0) {
|
||||
if (!data) {
|
||||
rprintf(FERROR, "Invalid literal token with no data [%s]\n", who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (DEBUG_GTE(DELTASUM, 3)) {
|
||||
rprintf(FINFO,"data recv %d at %s\n",
|
||||
i, big_num(offset));
|
||||
@@ -336,6 +390,11 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
}
|
||||
|
||||
i = -(i+1);
|
||||
if (i < 0 || i >= sum.count) {
|
||||
rprintf(FERROR, "Invalid block index %d (count=%ld) [%s]\n",
|
||||
i, (long)sum.count, who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
offset2 = i * (OFF_T)sum.blength;
|
||||
len = sum.blength;
|
||||
if (i == (int)sum.count-1 && sum.remainder != 0)
|
||||
@@ -343,6 +402,18 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
|
||||
|
||||
stats.matched_data += len;
|
||||
|
||||
/* A block match can only be honored if we actually mapped the
|
||||
* basis. If we didn't (basis open failed), the sender should
|
||||
* never have been told a basis existed -- treat it as a protocol
|
||||
* inconsistency rather than silently omitting these bytes from
|
||||
* the verification checksum (which yields a spurious failure) or
|
||||
* leaving a hole in the output. */
|
||||
if (!mapbuf) {
|
||||
rprintf(FERROR, "got a block match with no basis file for %s [%s]\n",
|
||||
full_fname(fname), who_am_i());
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
|
||||
if (DEBUG_GTE(DELTASUM, 3)) {
|
||||
rprintf(FINFO,
|
||||
"chunk[%d] of size %ld at %s offset=%s%s\n",
|
||||
@@ -435,7 +506,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(partialptr, fname) < 0) {
|
||||
if (do_rename_at(partialptr, fname) < 0) {
|
||||
rsyserr(FERROR_XFER, errno,
|
||||
"rename failed for %s (from %s)",
|
||||
full_fname(fname), partialptr);
|
||||
@@ -451,7 +522,10 @@ static void handle_delayed_updates(char *local_name)
|
||||
static void no_batched_update(int ndx, BOOL is_redo)
|
||||
{
|
||||
struct file_list *flist = flist_for_ndx(ndx, "no_batched_update");
|
||||
struct file_struct *file = flist->files[ndx - flist->ndx_start];
|
||||
struct file_struct *file;
|
||||
if (ndx < flist->ndx_start)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
file = flist->files[ndx - flist->ndx_start];
|
||||
|
||||
rprintf(FERROR_XFER, "(No batched update for%s \"%s\")\n",
|
||||
is_redo ? " resend of" : "", f_name(file, NULL));
|
||||
@@ -551,6 +625,8 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
progress_init();
|
||||
|
||||
while (1) {
|
||||
const char *basedir = NULL;
|
||||
|
||||
cleanup_disable();
|
||||
|
||||
/* This call also sets cur_flist. */
|
||||
@@ -586,6 +662,8 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
|
||||
if (ndx - cur_flist->ndx_start >= 0)
|
||||
file = cur_flist->files[ndx - cur_flist->ndx_start];
|
||||
else if (cur_flist->parent_ndx < 0)
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
else
|
||||
file = dir_flist->files[cur_flist->parent_ndx];
|
||||
fname = local_name ? local_name : f_name(file, fbuf);
|
||||
@@ -716,28 +794,34 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
fnamecmp = get_backup_name(fname);
|
||||
break;
|
||||
case FNAMECMP_FUZZY:
|
||||
if (fuzzy_basis == 0) {
|
||||
rprintf(FERROR_XFER, "rsync: refusing malicious fuzzy operation for %s\n", xname);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (file->dirname) {
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
} else
|
||||
fnamecmp = xname;
|
||||
basedir = file->dirname;
|
||||
}
|
||||
fnamecmp = xname;
|
||||
break;
|
||||
default:
|
||||
if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
|
||||
fnamecmp_type -= FNAMECMP_FUZZY + 1;
|
||||
if (file->dirname) {
|
||||
stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
|
||||
basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
|
||||
} else
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], file->dirname);
|
||||
basedir = fnamecmpbuf;
|
||||
} else {
|
||||
basedir = basis_dir[fnamecmp_type];
|
||||
}
|
||||
fnamecmp = xname;
|
||||
} else if (fnamecmp_type >= basis_dir_cnt) {
|
||||
rprintf(FERROR,
|
||||
"invalid basis_dir index: %d.\n",
|
||||
fnamecmp_type);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
} else
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
} else {
|
||||
basedir = basis_dir[fnamecmp_type];
|
||||
fnamecmp = fname;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!fnamecmp || (daemon_filter_list.head
|
||||
@@ -759,26 +843,33 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
fnamecmp = fname;
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
/* open the file (secure_basis_open tolerates an operator-trusted
|
||||
* absolute fnamecmp, e.g. an absolute --partial-dir basis) */
|
||||
fd1 = secure_basis_open(basedir, fnamecmp, O_RDONLY, 0);
|
||||
|
||||
if (fd1 == -1 && protocol_version < 29) {
|
||||
if (fnamecmp != fname) {
|
||||
fnamecmp = fname;
|
||||
fnamecmp_type = FNAMECMP_FNAME;
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
fd1 = do_open_nofollow(fnamecmp, O_RDONLY);
|
||||
}
|
||||
|
||||
if (fd1 == -1 && basis_dir[0]) {
|
||||
/* pre-29 allowed only one alternate basis */
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
|
||||
basis_dir[0], fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
basedir = basis_dir[0];
|
||||
fnamecmp = fname;
|
||||
fnamecmp_type = FNAMECMP_BASIS_DIR_LOW;
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (basedir) {
|
||||
// for the following code we need the full
|
||||
// path name as a single string
|
||||
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basedir, fnamecmp);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
}
|
||||
|
||||
one_inplace = inplace_partial && fnamecmp_type == FNAMECMP_PARTIAL_DIR;
|
||||
updating_basis_or_equiv = one_inplace
|
||||
|| (inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP));
|
||||
@@ -839,11 +930,21 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
/* We now check to see if we are writing the file "inplace" */
|
||||
if (inplace || one_inplace) {
|
||||
fnametmp = one_inplace ? partialptr : fname;
|
||||
fd2 = do_open(fnametmp, O_WRONLY|O_CREAT, 0600);
|
||||
/* When use_secure_symlinks is on (non-chroot daemon),
|
||||
* use secure open to prevent symlink race attacks where an
|
||||
* attacker could switch a directory to a symlink between
|
||||
* path validation and file open. */
|
||||
if (use_secure_symlinks)
|
||||
fd2 = secure_basis_open(NULL, fnametmp, O_WRONLY|O_CREAT, 0600);
|
||||
else
|
||||
fd2 = do_open(fnametmp, O_WRONLY|O_CREAT, 0600);
|
||||
#ifdef linux
|
||||
if (fd2 == -1 && errno == EACCES) {
|
||||
/* Maybe the error was due to protected_regular setting? */
|
||||
fd2 = do_open(fname, O_WRONLY, 0600);
|
||||
if (use_secure_symlinks)
|
||||
fd2 = secure_relative_open(NULL, fname, O_WRONLY, 0600);
|
||||
else
|
||||
fd2 = do_open(fname, O_WRONLY, 0600);
|
||||
}
|
||||
#endif
|
||||
if (fd2 == -1) {
|
||||
@@ -895,7 +996,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
recv_ok = -1;
|
||||
else if (fnamecmp == partialptr) {
|
||||
if (!one_inplace)
|
||||
do_unlink(partialptr);
|
||||
do_unlink_at(partialptr);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
} else if (keep_partial && partialptr && (!one_inplace || delay_updates)) {
|
||||
@@ -904,7 +1005,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
"Unable to create partial-dir for %s -- discarding %s.\n",
|
||||
local_name ? local_name : f_name(file, NULL),
|
||||
recv_ok ? "completed file" : "partial file");
|
||||
do_unlink(fnametmp);
|
||||
do_unlink_at(fnametmp);
|
||||
recv_ok = -1;
|
||||
} else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
|
||||
file, recv_ok, !partial_dir))
|
||||
@@ -915,7 +1016,7 @@ int recv_files(int f_in, int f_out, char *local_name)
|
||||
} else
|
||||
partialptr = NULL;
|
||||
} else if (!one_inplace)
|
||||
do_unlink(fnametmp);
|
||||
do_unlink_at(fnametmp);
|
||||
|
||||
cleanup_disable();
|
||||
|
||||
|
||||
11
rsync-web/.gitignore
vendored
Normal file
11
rsync-web/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/.xvpics
|
||||
/doxygen/
|
||||
/tech_report/IMG_PARAMS.dir
|
||||
/tech_report/IMG_PARAMS.pag
|
||||
/upload
|
||||
/netware
|
||||
/pre-change
|
||||
/index.html-*
|
||||
/rsync-and-debian/rsync-and-debian.html
|
||||
/rsync-and-debian/rsync-and-debian.ps
|
||||
/badge.svg
|
||||
674
rsync-web/COPYING.html
Normal file
674
rsync-web/COPYING.html
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <<a href="https://fsf.org/">https://fsf.org/</a>>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <<a href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/</a>>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<<a href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/</a>>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<<a href="https://www.gnu.org/philosophy/why-not-lgpl.html">https://www.gnu.org/philosophy/why-not-lgpl.html</a>>.
|
||||
340
rsync-web/COPYING2.html
Normal file
340
rsync-web/COPYING2.html
Normal file
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
<hr>
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
<hr>
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
<hr>
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
<hr>
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
<hr>
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
284
rsync-web/FAQ.html
Normal file
284
rsync-web/FAQ.html
Normal file
@@ -0,0 +1,284 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync FAQ</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">Frequently Asked Questions</H2>
|
||||
|
||||
<table><tr valign=top><td><ul>
|
||||
<li><a href="#1">the transfer fails to finish</a><br>
|
||||
<li><a href="#2">rsync recopies the same files</a><br>
|
||||
<li><a href="#3">is your shell clean</a><br>
|
||||
<li><a href="#4">memory usage</a><br>
|
||||
<li><a href="#5">out of memory</a><br>
|
||||
<li><a href="#6">rsync through a firewall</a><br>
|
||||
<li><a href="#7">rsync and cron</a><br>
|
||||
</ul></td><td> </td><td><ul>
|
||||
<li><a href="#8">rsync: Command not found</a><br>
|
||||
<li><a href="#9">spaces in filenames</a><br>
|
||||
<li><a href="#10">ignore "vanished files" warning</a><br>
|
||||
<li><a href="#11">read-only file system</a><br>
|
||||
<li><a href="#12">multiplexing overflow 101:7104843</a><br>
|
||||
<li><a href="#13">inflate (token) returned -5</a><br>
|
||||
</ul></td></tr></table>
|
||||
|
||||
<hr>
|
||||
<h3><a name=1>the transfer fails to finish</a></h3>
|
||||
|
||||
<p>If you get an error like one of these:
|
||||
|
||||
<pre>rsync: error writing 4 unbuffered bytes - exiting: Broken pipe
|
||||
rsync error: error in rsync protocol data stream (code 12) at io.c(463)
|
||||
</pre>
|
||||
|
||||
<p>or
|
||||
|
||||
<pre>rsync: connection unexpectedly closed (24 bytes read so far)
|
||||
rsync error: error in rsync protocol data stream (code 12) at io.c(342)
|
||||
</pre>
|
||||
|
||||
<p>please read the <a href="issues.html">issues and debugging page</a>
|
||||
for details on how you can try to figure out what is going wrong.
|
||||
|
||||
<hr>
|
||||
<h3><a name=2>rsync recopies the same files</a></h3>
|
||||
|
||||
<p>Some people occasionally report that rsync copies too many files when
|
||||
they expect it to copy only a few. In most cases the explanation is
|
||||
that you forgot to include the --times (-t) option in the original copy,
|
||||
so rsync is forced to (efficiently) transfer every file that differs in
|
||||
its modified time to discover what data (if any) has changed.
|
||||
|
||||
<p>Another common cause involves sending files to an Microsoft filesystem:
|
||||
if the file's modified time is an odd value but the receiving filesystem
|
||||
can only store even values, then rsync will re-transfer too many files.
|
||||
You can avoid this by specifying the --modify-window=1 option.
|
||||
|
||||
<p>Yet another periodic case can happen when daylight-savings time
|
||||
changes if your OS+filesystem saves file times in local time instead of
|
||||
UTC. For a full explanation of this and some suggestions on how to
|
||||
avoid them problem, see <a href="daylight-savings.html">this document</a>.
|
||||
|
||||
<p>Something else that can trip up rsync is a filesystem changeing the
|
||||
filename behind the scenes. This can happen when a filesystem changes
|
||||
an all-uppercase name into lowercase, or when it decomposes UTF-8 behind
|
||||
your back.
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p>An example of the latter can occur with HFS+ on Mac OS X: if you
|
||||
copy a directory with a file that has a UTF-8 character sequence in it,
|
||||
say a 2-byte umlaut-u (\0303\0274), the file will get that character
|
||||
stored by the filesystem using 3 bytes (\0165\0314\0210), and rsync will
|
||||
not know that these differing filenames are the same file (it will, in
|
||||
fact, remove a prior copy of the file if --delete is enabled, and then
|
||||
recreate it).
|
||||
|
||||
<p>You can avoid a charset problem by passing an appropriate --iconv
|
||||
option to rsync that tells it what character-set the source files are,
|
||||
and what character-set the destination files get stored in. For
|
||||
instance, the above Mac OS X problem would be dealt with by using
|
||||
--iconv=UTF-8,UTF8-MAC (UTF8-MAC is a pseudo-charset recognized by Mac
|
||||
OS X iconv in which all characters are decomposed).
|
||||
|
||||
</blockquote>
|
||||
|
||||
<p>If you think that rsync is copying too many files, look at the
|
||||
itemized output (-i) to see why rsync is doing the update (e.g. the 't'
|
||||
flag indicates that the time differs, or all pluses indicates that rsync
|
||||
thinks the file doesn't exist). You can also look at the stats produced
|
||||
with -v and see if rsync is really sending all the data. See also the
|
||||
--checksum (-c) option for one way to avoid the extra copying of files
|
||||
that don't have synchronized modified times (but keep in mind that the
|
||||
-c option eats lots of disk I/O, and can be rather slow).
|
||||
|
||||
<hr>
|
||||
<h3><a name=3>is your shell clean</a></h3>
|
||||
|
||||
<p>The "is your shell clean" message and the "protocol mismatch" message
|
||||
are usually caused by having some sort of program in your .cshrc, .profile,
|
||||
.bashrc or equivalent file that writes a message every time you connect
|
||||
using a remote-shell program (such as ssh or rsh). Data written in this
|
||||
way corrupts the rsync data stream. rsync detects this at startup and
|
||||
produces those error messages. However, if you are using rsync-daemon
|
||||
syntax (host::path or rsync://) without using a remote-shell program (no
|
||||
--rsh or -e option), there is not remote-shell program involved, and the
|
||||
problem is probably caused by an error on the daemon side (so check the
|
||||
daemon logs).
|
||||
|
||||
<p>A good way to test if your remote-shell connection is clean is to try
|
||||
something like this (use ssh or rsh, as appropriate):
|
||||
|
||||
<blockquote><pre>ssh remotesystem /bin/true > test.dat</pre></blockquote>
|
||||
|
||||
<p>That should create a file called test.dat with nothing in it. If
|
||||
test.dat is not of zero length then your shell is not clean. Look at the
|
||||
contents of test.dat to see what was sent. Look at all the startup files on
|
||||
remotesystem to try and find the problem.
|
||||
|
||||
<hr>
|
||||
<h3><a name=4>memory usage</a></h3>
|
||||
|
||||
<p>Rsync versions before 3.0.0 always build the entire list of files to be
|
||||
transferred at the beginning and hold it in memory for the entire run. Rsync
|
||||
needs about 100 bytes to store all the relevant information for one file,
|
||||
so (for example) a run with 800,000 files would consume about 80M of
|
||||
memory. -H and --delete increase the memory usage further.
|
||||
|
||||
<p>Version 3.0.0 slightly reduced the memory used per file by not storing fields
|
||||
not needed for a particular file. It also introduced an incremental recursion
|
||||
mode that builds the file list in chunks and holds each chunk in memory only as
|
||||
long as it is needed. This mode dramatically reduces memory usage, but it
|
||||
only works provided that both sides are 3.0.0 or newer and certain options that
|
||||
rsync currently can't handle in this mode are not being used.
|
||||
|
||||
<hr>
|
||||
<h3><a name=5>out of memory</a></h3>
|
||||
|
||||
<p>The usual reason for "out of memory" when running rsync is that you are
|
||||
transferring a _very_ large number of files. The size of the files doesn't
|
||||
matter, only the total number of files. If memory is a problem, first try to
|
||||
use the incremental recursion mode: upgrade both sides to rsync 3.0.0 or
|
||||
newer and avoid options that disable incremental recursion (e.g., use
|
||||
<tt>--delete-delay</tt> instead of <tt>--delete-after</tt>). If this is not
|
||||
possible, you can break the rsync run into smaller chunks operating on
|
||||
individual subdirectories using <tt>--relative</tt> and/or exclude rules.
|
||||
|
||||
<hr>
|
||||
<h3><a name=6>rsync through a firewall</a></h3>
|
||||
|
||||
<p>If you have a setup where there is no way to directly connect two
|
||||
systems for an rsync transfer, there are several ways to get a firewall
|
||||
system to act as an intermediary in the transfer. You'll find full details
|
||||
on the <a href="firewall.html">firewall methods</a> page.
|
||||
|
||||
<hr>
|
||||
<h3><a name=7>rsync and cron</a></h3>
|
||||
|
||||
<p>On some systems (notably SunOS4) cron supplies what looks like a socket
|
||||
to rsync, so rsync thinks that stdin is a socket. This means that if you
|
||||
start rsync with the --daemon switch from a cron job you end up rsync
|
||||
thinking it has been started from inetd. The fix is simple—just
|
||||
redirect stdin from /dev/null in your cron job.
|
||||
|
||||
<hr>
|
||||
<h3><a name=8>rsync: Command not found</a></h3>
|
||||
|
||||
<p>This error is produced when the remote shell is unable to locate the rsync
|
||||
binary in your path. There are 3 possible solutions:
|
||||
|
||||
<ol>
|
||||
|
||||
<li>install rsync in a "standard" location that is in your remote path.
|
||||
|
||||
<li>modify your .cshrc, .bashrc etc on the remote system to include the path
|
||||
that rsync is in
|
||||
|
||||
<li>use the --rsync-path option to explicitly specify the path on the
|
||||
remote system where rsync is installed
|
||||
|
||||
</ol>
|
||||
|
||||
<p>You may echo find the command:
|
||||
|
||||
<blockquote><pre>ssh host 'echo $PATH'</pre></blockquote>
|
||||
|
||||
<p>for determining what your remote path is.
|
||||
|
||||
<hr>
|
||||
<h3><a name=9>spaces in filenames</a></h3>
|
||||
|
||||
<p>Can rsync copy files with spaces in them?
|
||||
|
||||
<p>Short answer: Yes, rsync can handle filenames with spaces.
|
||||
|
||||
<p>Long answer:
|
||||
|
||||
<p>Rsync handles spaces just like any other unix command line application.
|
||||
Within the code spaces are treated just like any other character so a
|
||||
filename with a space is no different from a filename with any other
|
||||
character in it.
|
||||
|
||||
<p>The problem of spaces is in the argv processing done to interpret the
|
||||
command line. As with any other unix application you have to escape spaces
|
||||
in some way on the command line or they will be used to separate arguments.
|
||||
|
||||
<p>It is slightly trickier in rsync (and other remote-copy programs like
|
||||
scp) because rsync sends a command line to the remote system to launch the
|
||||
peer copy of rsync (this assumes that we're not talking about daemon mode,
|
||||
which is not affected by this problem because no remote shell is involved
|
||||
in the reception of the filenames). The command line is interpreted by the
|
||||
remote shell and thus the spaces need to arrive on the remote system
|
||||
escaped so that the shell doesn't split such filenames into multiple
|
||||
arguments.
|
||||
|
||||
<p>For example:
|
||||
|
||||
<blockquote><pre>rsync -av host:'a long filename' /tmp/</pre></blockquote>
|
||||
|
||||
<p>This is usually a request for rsync to copy 3 files from the remote
|
||||
system, "a", "long", and "filename" (the only exception to this is for a
|
||||
system running a shell that does not word-split arguments in its commands,
|
||||
and that is exceedingly rare). If you wanted to request a single file with
|
||||
spaces, you need to get some kind of space-quoting characters to the remote
|
||||
shell that is running the remote rsync command. The following commands
|
||||
should all work:
|
||||
|
||||
<blockquote><pre>rsync -av host:'"a long filename"' /tmp/
|
||||
rsync -av host:'a\ long\ filename' /tmp/
|
||||
rsync -av host:a\\\ long\\\ filename /tmp/</pre></blockquote>
|
||||
|
||||
<p>You might also like to use a '?' in place of a space as long as there
|
||||
are no other matching filenames than the one with spaces (since '?' matches
|
||||
any character):
|
||||
|
||||
<blockquote><pre>rsync -av host:a?long?filename /tmp/</pre></blockquote>
|
||||
|
||||
<p>As long as you know that the remote filenames on the command line
|
||||
are interpreted by the remote shell then it all works fine.
|
||||
|
||||
<hr>
|
||||
<h3><a name=10>ignore "vanished files" warning</a></h3>
|
||||
|
||||
<p>Some folks would like to ignore the "vanished files" warning, which
|
||||
manifests as an exit-code 24. The easiest way to do this is to create
|
||||
a shell script wrapper. For instance, name this something like
|
||||
"rsync-no24":
|
||||
|
||||
<blockquote><pre>#!/bin/sh
|
||||
rsync "$@"
|
||||
e=$?
|
||||
if test $e = 24; then
|
||||
exit 0
|
||||
fi
|
||||
exit $e</pre></blockquote>
|
||||
|
||||
<hr>
|
||||
<h3><a name=11>read-only file system</a></h3>
|
||||
|
||||
<p>If you get "Read-only file system" as an error when sending to a rsync
|
||||
daemon then you probably forgot to set "read only = no" for that module.
|
||||
|
||||
<hr>
|
||||
<h3><a name=12>multiplexing overflow 101:7104843</a></h3>
|
||||
|
||||
<p>This mysterious error, or the similar "invalid message 101:7104843", can
|
||||
happen if one of the rsync processes is killed for some reason and a message
|
||||
beginning with the four characters "Kill" gets inserted into the protocol
|
||||
stream as a result. To solve the problem, you'll need to figure out why rsync
|
||||
is being killed.
|
||||
|
||||
<hr>
|
||||
<h3><a name=13>inflate (token) returned -5</a></h3>
|
||||
|
||||
This error means that rsync failed to handle an expected error from the
|
||||
compression code for a file that happened to be transferred with a block size
|
||||
of 32816 bytes. You can avoid this issue for the affected file by transferring
|
||||
it with a manually-set block size (e.g. --block-size=33000), or by upgrading
|
||||
the receiving side to rsync 3.0.7.
|
||||
|
||||
<hr>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
16
rsync-web/GPL.html
Normal file
16
rsync-web/GPL.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync's license</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
Beginning with 3.0.0, rsync is available under the <b>GNU General Public
|
||||
License version 3</b>. <i>(Older releases were available under the
|
||||
<a href="GPL2.html">GPL version 2</a>.)</i>
|
||||
|
||||
<pre><small>
|
||||
<!--#include virtual="COPYING.html" -->
|
||||
</small></pre>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
15
rsync-web/GPL2.html
Normal file
15
rsync-web/GPL2.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync's license</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
Releases <b>prior</b> to 3.0.0 were released under the
|
||||
<b>GNU General Public License version 2</b>:
|
||||
|
||||
<pre><small>
|
||||
<!--#include virtual="COPYING2.html" -->
|
||||
</small></pre>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
36
rsync-web/backup.txt
Normal file
36
rsync-web/backup.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script does personal backups to a rsync backup server. You will end up
|
||||
# with a 7 day rotating incremental backup. The incrementals will go
|
||||
# into subdirectories named after the day of the week, and the current
|
||||
# full backup goes into a directory called "current"
|
||||
# tridge@linuxcare.com
|
||||
|
||||
# directory to backup
|
||||
BDIR=/home/$USER
|
||||
|
||||
# excludes file - this contains a wildcard pattern per line of files to exclude
|
||||
EXCLUDES=$HOME/cron/excludes
|
||||
|
||||
# the name of the backup machine
|
||||
BSERVER=owl
|
||||
|
||||
# your password on the backup server
|
||||
export RSYNC_PASSWORD=XXXXXX
|
||||
|
||||
|
||||
########################################################################
|
||||
|
||||
BACKUPDIR=`date +%A`
|
||||
OPTS="--force --ignore-errors --delete-excluded --exclude-from=$EXCLUDES
|
||||
--delete --backup --backup-dir=/$BACKUPDIR -a"
|
||||
|
||||
export PATH=$PATH:/bin:/usr/bin:/usr/local/bin
|
||||
|
||||
# the following line clears the last weeks incremental directory
|
||||
[ -d $HOME/emptydir ] || mkdir $HOME/emptydir
|
||||
rsync --delete -a $HOME/emptydir/ $BSERVER::$USER/$BACKUPDIR/
|
||||
rmdir $HOME/emptydir
|
||||
|
||||
# now the actual transfer
|
||||
rsync $OPTS $BDIR $BSERVER::$USER/current
|
||||
BIN
rsync-web/bar1.jpg
Normal file
BIN
rsync-web/bar1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
4
rsync-web/bin/badge-update
Executable file
4
rsync-web/bin/badge-update
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
rm -f badge.svg
|
||||
wget https://github.com/RsyncProject/rsync/workflows/build/badge.svg
|
||||
rsync -aiic --inplace --remove-source-files badge.svg $SAMBA_HOST:/home/httpd/html/rsync/
|
||||
7
rsync-web/bin/upload
Executable file
7
rsync-web/bin/upload
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
if [ -d rsync-and-debian ]; then
|
||||
rsync -aviOHFFc --del -f._filt . $SAMBA_HOST:/home/httpd/html/rsync/ "${@}"
|
||||
else
|
||||
echo "Run this from the root of the html hierarchy."
|
||||
exit 1
|
||||
fi
|
||||
69
rsync-web/bug-tracking.html
Normal file
69
rsync-web/bug-tracking.html
Normal file
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync bug-tracking</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">rsync bug-tracking</H2>
|
||||
|
||||
<p> Please use this checklist combined with the help on the
|
||||
<a href="issues.html">issues and debugging</a> page before
|
||||
reporting a bug. Thanks!
|
||||
|
||||
<ul>
|
||||
|
||||
<li> If you're not using the latest released version, please upgrade before
|
||||
reporting a bug.
|
||||
|
||||
<li> If you're using the latest released version, consult the
|
||||
<a href="https://github.com/RsyncProject/rsync/blob/master/NEWS.md">NEWS file from the git repository</a> to see if what
|
||||
you're seeing has already been handled in the version under development.
|
||||
|
||||
<li> It is also helpful to search the bug reports at the
|
||||
<a href="https://github.com/RsyncProject/rsync/issues?q=is%3Aissue">GitHub issues tracker</a>
|
||||
to see if the problem is already known.
|
||||
|
||||
<li> See also the <a href="issues.html">issues and debugging</a> page to
|
||||
help you figure out if what you're seeing is a known bug and perhaps to
|
||||
help diagnose what is going wrong.
|
||||
|
||||
<li> Discuss the bug on the
|
||||
<a href="https://lists.samba.org/mailman/listinfo/rsync">rsync mailing list</a>
|
||||
(which is at <tt>rsync@lists.samba.org</tt>) to help you figure out if what
|
||||
you're seeing is really a bug or a mistake.
|
||||
|
||||
<li>There are several patches for features that are under consideration that
|
||||
can be found in the <a href="https://github.com/RsyncProject/rsync-patches">rsync-patches repo</a>.
|
||||
|
||||
<li> If you haven't already done so, please take a couple of minutes to read Simon Tatham's
|
||||
<a href="https://www.chiark.greenend.org.uk/~sgtatham/bugs.html">advice on how to report bugs</a>.
|
||||
|
||||
</ul>
|
||||
|
||||
<p> To report a bug or make suggestions, use one of these methods:
|
||||
|
||||
<ul>
|
||||
|
||||
<li> The mailing list (mentioned above) is a good resource for discussing
|
||||
bugs and suggesting new features. It accepts patches (typically as MIME
|
||||
attachments), but for fixes is often easier to attach a patch to an
|
||||
appropriate GitHub issue report or use a pull request. Note that there is
|
||||
no mandate to use pull requests for patches, as that can be a pretty high
|
||||
bar of git know-how that not everyone needs to be familiar with.
|
||||
|
||||
<li> If you'd like to see a bug-report or feature-request get officially noted,
|
||||
<a href="https://github.com/RsyncProject/rsync/issues">create an issue on GitHub</a>
|
||||
(this does require that you have created a GitHub account). If you want to
|
||||
stay abreast of what's going on with the issues, make use of the GitHub
|
||||
subscriptions to pick and choose what kind of notifications you want to
|
||||
receive (e.g. just a single issue, all issues, all rsync activity, etc.).
|
||||
|
||||
<li>For security issues please send email
|
||||
to <a href="mailto:rsync.project@gmail.com">rsync.project@gmail.com</a>
|
||||
with details of the issue
|
||||
</ul>
|
||||
|
||||
<p> Thanks for helping out!
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
7
rsync-web/bugtracking.html
Normal file
7
rsync-web/bugtracking.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync bug-tracking</TITLE>
|
||||
</HEAD>
|
||||
|
||||
<meta http-equiv="refresh" content="0;URL='https://rsync.samba.org/bug-tracking.html'" />
|
||||
7
rsync-web/bugzilla.html
Normal file
7
rsync-web/bugzilla.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync bug-tracking</TITLE>
|
||||
</HEAD>
|
||||
|
||||
<meta http-equiv="refresh" content="0;URL='https://rsync.samba.org/bug-tracking.html'" />
|
||||
11
rsync-web/convert-gpl
Normal file
11
rsync-web/convert-gpl
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/perl
|
||||
use strict;
|
||||
|
||||
while (<>) {
|
||||
s/&/&/g;
|
||||
s/</</g;
|
||||
s/>/>/g;
|
||||
s//<hr>/;
|
||||
s#(<)(https?:.*?)(>)#$1<a href="$2">$2</a>$3#g;
|
||||
print $_;
|
||||
}
|
||||
183
rsync-web/daylight-savings.html
Normal file
183
rsync-web/daylight-savings.html
Normal file
@@ -0,0 +1,183 @@
|
||||
<HTML><HEAD>
|
||||
<TITLE>DST change and date comparisons</TITLE>
|
||||
</HEAD><BODY>
|
||||
|
||||
<p><small><i>J.W. Schultz wrote the following text in a
|
||||
<a href="http://www.cygwin.com/ml/cygwin/2003-10/msg00995.html">post</a>
|
||||
to the cygwin mailing list. It has been slightly edited and beautified
|
||||
for inclusion here.</i></small>
|
||||
|
||||
<h1>How the DST Change can adversely affect FAT filesystems</h1>
|
||||
|
||||
<p>
|
||||
The vernal and autumnal transitions to and from daylight-savings
|
||||
time have important implications
|
||||
for those with Microsoft systems and use utilities that
|
||||
compare file timestamps on different filesystem types or
|
||||
with filesystems on other operating systems that can lead to
|
||||
a problem in how the file's date is handled.
|
||||
This problem lies in the way FAT filesystems stores
|
||||
timestamps and how Windows converts between local time and
|
||||
UTC.
|
||||
|
||||
<h2>Background</h2>
|
||||
|
||||
<p>
|
||||
In UNIX and UNIX-like systems (such as Linux) file timestamps
|
||||
are stored in UTC (universal time) and are only converted to
|
||||
local-time by user-space programs for display purposes. At
|
||||
the system call level all time values are in UTC and
|
||||
utilities that compare timestamps do so in UTC. Also, the
|
||||
standard UTC->local and local->UTC conversion functions are
|
||||
aware of DST and conversions reflect this so that if a
|
||||
timestamp was recorded during ST it will be converted using
|
||||
the ST offset even when the current system time is DST.
|
||||
|
||||
<p>
|
||||
In Windows things are not so simple. Windows operates in
|
||||
local-time. Timestamps in the various FAT derived
|
||||
filesystems are stored in local-time. Timestamps in NTFS
|
||||
filesystems are stored in UTC. This inconsistency is
|
||||
further complicated by the fact that the conversion routines
|
||||
used are not DST aware. Instead of being DST aware the
|
||||
system has a fixed offset to convert between local-time and
|
||||
UTC regardless of the date in the timestamp. This fixed
|
||||
offset is calculated at boot time and only changed when
|
||||
systems transition to or from DST. As a result the apparent
|
||||
modification time of a file on NTFS as reported in a windows
|
||||
utility will change by one hour when reported in local-time
|
||||
and FAT based files when reported in UTC.
|
||||
|
||||
<p>
|
||||
The difficulty that this produces is that any utilities that
|
||||
compare timestamps between FAT and NTFS filesystems or
|
||||
between Windows and other platforms will view files that
|
||||
have not changed as having a different modified time. Among other things
|
||||
this will affect rsync, rdiff, unison, wget, and make. However,
|
||||
for the purposes of this document, we will only discuss rsync.
|
||||
|
||||
<p>
|
||||
With the reduced cost of hard disks many newer backup
|
||||
systems are using hard disk based storage and take advantage
|
||||
of timestamp comparison to detect file changes for the sake
|
||||
of efficiency. Rsync is probably premier in this role and
|
||||
is used by a fair number of free and even commercial backup
|
||||
systems as well as being the basis for many home-brew backup
|
||||
solutions.
|
||||
|
||||
<p>
|
||||
With rsync and similar systems the effect of this is that
|
||||
every file will appear to have been changed. The result is
|
||||
any space savings associated with linking (--link-dest) or
|
||||
with decremental backup approaches (--compare-dest and
|
||||
--backup-dir) will be defeated. Perhaps worse, because
|
||||
every file will appear to have changed the time required to
|
||||
do a backup or a non-backup rsync will be much longer than
|
||||
normal. In some cases backups that normally complete in
|
||||
less than one hour can take several days.
|
||||
|
||||
<p>
|
||||
So what can be done about it? Several things, there are
|
||||
ways to merely mitigate the problem, to correct it and finally
|
||||
to prevent the problem entirely.
|
||||
|
||||
<h2>Mitigation</h2>
|
||||
|
||||
<p>
|
||||
Rsync has a --modify-window option. Many of you already use
|
||||
--modify-window=1 to cope with the fact that windows often
|
||||
stores timestamps with a two second resolution. Using a
|
||||
--modify-window=3601 will cause rsync to ignore timestamp
|
||||
differences of up to one hour.
|
||||
|
||||
<p>
|
||||
This if often not particularly dangerous because a file would have to
|
||||
be changed, synced and changed again without changing size
|
||||
within a single hour and have no subsequent changes for this copy
|
||||
to miss a file change. However, for some systems any such risk is
|
||||
unacceptable, so other solutions are needed.
|
||||
|
||||
<h2>Correcting the Timestamps</h2>
|
||||
|
||||
<p>
|
||||
There are two ways to correct.
|
||||
|
||||
<p>For the first run after the time change, you can run rsync with the
|
||||
--checksum option in order to ensure that only files that have a changed
|
||||
checksum get transferred, and update the modified time on all unchanged
|
||||
files. This option has the drawback that it increases disk I/O by
|
||||
a large amount on both the sending and receiving side, slowing down the
|
||||
copy.
|
||||
|
||||
<p>
|
||||
The other way to correct things is to change the timestamps
|
||||
on the files on the backup server before doing a copy after a
|
||||
time change has occurred.
|
||||
|
||||
<p>
|
||||
Included here is an example perl script that will change the
|
||||
timestamps of files in a list on standard-input. Whether
|
||||
you use a positive or negative shift will depend on which
|
||||
end you decide to adjust.
|
||||
|
||||
<p>
|
||||
This is an example of how to use the script:
|
||||
|
||||
<blockquote><pre>
|
||||
touch -d '01:00 13-apr-03' /tmp/cmpfile
|
||||
find . -type f ! -newer /tmp/cmpfile | shifttime.pl 3600
|
||||
</pre></blockquote>
|
||||
|
||||
<hr>
|
||||
<pre>
|
||||
#!/usr/bin/perl
|
||||
use strict;
|
||||
|
||||
my $offset = shift() + 0;
|
||||
die "Usage: $0 OFFSET_SECONDS\n" unless $offset;
|
||||
|
||||
while (<>) {
|
||||
chomp;
|
||||
my $mtime = (stat $_)[9];
|
||||
next unless $mtime;
|
||||
$mtime += $offset;
|
||||
utime $mtime, $mtime, $_;
|
||||
}
|
||||
</pre>
|
||||
<hr>
|
||||
|
||||
<h2>Prevention</h2>
|
||||
|
||||
<p>
|
||||
To prevent the problem in the first place you need to
|
||||
prevent changing to DST. This can be done by either running
|
||||
the windows system in UTC, by disabling DST and changing
|
||||
the system time manually twice each year, or by avoiding the use
|
||||
of the FAT filesystem (perhaps by switching to a different OS).
|
||||
|
||||
|
||||
<h2>Notes and References</h2>
|
||||
|
||||
<p>
|
||||
Here are some references that Wayne Piekarski collected
|
||||
while researching this problem. They contain a lot of
|
||||
information about the ways that Windows deals with
|
||||
timestamps on NTFS and FAT filesystems.
|
||||
|
||||
<p>
|
||||
<a href="http://optics.ph.unimelb.edu.au/help/rsync/rsync_pc1.html#gotchas">http://optics.ph.unimelb.edu.au/help/rsync/rsync_pc1.html#gotchas</a></br>
|
||||
<a href="http://list-archive.xemacs.org/xemacs-nt/199911/msg00130.html">http://list-archive.xemacs.org/xemacs-nt/199911/msg00130.html</a></br>
|
||||
<a href="http://p2p.wrox.com/archive/c_plus_plus_programming/2001-06/53.asp">http://p2p.wrox.com/archive/c_plus_plus_programming/2001-06/53.asp</a></br>
|
||||
<a href="http://www.codeproject.com/datetime/dstbugs.asp">http://www.codeproject.com/datetime/dstbugs.asp</a></br>
|
||||
<a href="http://support.microsoft.com/default.aspx?scid=kb;[LN];158588">http://support.microsoft.com/default.aspx?scid=kb;[LN];158588</a>
|
||||
|
||||
<p>
|
||||
I wish to thank Wayne Piekarski for having copiled the
|
||||
references and also supplying some additional insights.
|
||||
|
||||
<p>
|
||||
Permission is granted without reservation reprint and
|
||||
distribute this in whole and in part to any interested
|
||||
parties.
|
||||
|
||||
<p>—J.W. Schultz
|
||||
15
rsync-web/doc-resources.html
Normal file
15
rsync-web/doc-resources.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- This file gets included into the resources and documentation pages -->
|
||||
|
||||
<li> A nice tutorial on <a href="https://linuxize.com/post/how-to-setup-passwordless-ssh-login/">setting up ssh to avoid password prompts</a>.
|
||||
|
||||
<li> Karsten Thygesen has written a doc on how to setup
|
||||
<A HREF="http://dslab.lzu.edu.cn:8080/members/wangbj/wangbaojun/howtos/rsync-mirror-HOWTO/">anonymous rsync servers</A>.
|
||||
|
||||
<li> Anthony Wesley has written a doc on
|
||||
<A HREF="/win95.txt">how to build rsync for Windows95</A>.
|
||||
|
||||
<li> Mike McHenry has written up some info on how to get <a href="nt.html">rsync working under NT</a>.
|
||||
|
||||
<li> Michael Holve has written a very useful <a href="http://everythinglinux.org/rsync/">rsync tutorial</a>.
|
||||
|
||||
<li> Daniel Teklu has a detailed HOWTO on <a href="http://www.netbits.us/docs/stunnel_rsync.html">rsync and stunnel</a>.
|
||||
54
rsync-web/documentation.html
Normal file
54
rsync-web/documentation.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync documentation</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">documentation</H2>
|
||||
|
||||
<ul>
|
||||
|
||||
<li> An html version of the <a href="https://download.samba.org/pub/rsync/rsync.1"><b>rsync</b>(1) manpage</a>.
|
||||
|
||||
<li> An html version of the
|
||||
<a href="https://download.samba.org/pub/rsync/rsync-ssl.1"><b>rsync-ssl</b>(1) manpage</a>.
|
||||
|
||||
<li> An html version of the
|
||||
<a href="https://download.samba.org/pub/rsync/rsyncd.conf.5"><b>rsyncd.conf</b>(5) manpage</a>.
|
||||
|
||||
<li> An html version of the
|
||||
<a href="https://download.samba.org/pub/rsync/rrsync.1"><b>rrsync</b>(1) manpage</a>
|
||||
|
||||
<li> The <a href="FAQ.html">FAQ</a> (frequently asked questions list).
|
||||
|
||||
<!--#include virtual="doc-resources.html" -->
|
||||
|
||||
<!--
|
||||
<li>Cross-referenced
|
||||
<a href="doxygen/head/files.html">rsync source code</a> produced by
|
||||
<a href="http://doxygen.org/">Doxygen</a>.
|
||||
-->
|
||||
|
||||
<li> A html version of the original
|
||||
<a href="tech_report/">rsync technical report</a>.
|
||||
|
||||
<li> A copy of Andrew Tridgell's
|
||||
<a href="http://samba.org/~tridge/phd_thesis.pdf">PhD thesis</a>
|
||||
(which includes three chapters on rsync).
|
||||
|
||||
<li> A nice page on <a href="how-rsync-works.html">how rsync works</a>.
|
||||
|
||||
<li> A copy of John Langford's thesis on
|
||||
<a href="http://www-2.cs.cmu.edu/~jcl/research/mrsync/mrsync.ps">Multiround rsync</a>,
|
||||
which is not used in rsync, but is interesting none-the-less.
|
||||
|
||||
<li>A <a href="http://www.devshed.com/c/a/Administration/File-Synchronization-With-Rsync/">DevShed tutorial on rsync</a>.
|
||||
|
||||
<li> <a href="rsync-and-debian/">Notes on rsync and Debian mirrors</a>.
|
||||
|
||||
</ul>
|
||||
|
||||
<p> <i>(Some of the above items are also listed on the <a href="resources.html">rsync resources page</a>.</i>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
127
rsync-web/download.html
Normal file
127
rsync-web/download.html
Normal file
@@ -0,0 +1,127 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<html>
|
||||
<head>
|
||||
<TITLE>rsync download</TITLE>
|
||||
</head>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<h2 align="center">rsync download</h2>
|
||||
|
||||
<div style="float: right">
|
||||
<a href="https://github.com/RsyncProject/rsync/actions">
|
||||
<img src="badge.svg">
|
||||
</a></div>
|
||||
|
||||
<h2>Source-code releases</h2>
|
||||
|
||||
<p>You can grab the latest source code and other related files in a variety of ways:
|
||||
|
||||
<ul>
|
||||
|
||||
<li><p>The latest version is linked on the <a href="https://rsync.samba.org/">main page</a>.
|
||||
|
||||
<li><p>A directory listing of these latest files and various historical release and diff files
|
||||
are available via <a href="https://download.samba.org/pub/rsync/">this web page</a> and
|
||||
via <i>anonymous SSL rsync</i> using this command:
|
||||
<p><code><small>rsync-ssl rsync://download.samba.org/rsyncftp/</small></code></p>
|
||||
|
||||
<li><p>You can also get .zip and .tar.gz versions of the various git repo's release
|
||||
tags via the <a href="https://github.com/RsyncProject/rsync/tags">rsync GitHub tags page</a>
|
||||
and the associated patches via the
|
||||
<a href="https://github.com/RsyncProject/rsync-patches/tags">rsync-patches GitHub tags page</a>.
|
||||
Keep in mind that these git-derived files do NOT come with the extra generated files that are included
|
||||
in the official release tar files.
|
||||
|
||||
<li><p>You can browse the very latest source files, clone the source using git, or download a .zip file of the latest
|
||||
master branch from <a href="https://github.com/RsyncProject/rsync">rsync's GitHub page</a>.
|
||||
|
||||
<li><p>The <a href="https://git.samba.org/?p=rsync.git">Samba git repo</a> is also available,
|
||||
though it might lag behind the GitHub repo every now and then.
|
||||
|
||||
</ul>
|
||||
|
||||
Once you have the source, read the <a
|
||||
href="https://download.samba.org/pub/rsync/INSTALL">INSTALL.md</a> file for
|
||||
details on some development libraries that you will need to build it.
|
||||
|
||||
<h2>The GPG Signing Key</h2>
|
||||
|
||||
The GPG signing key that is used to sign the release files is available from the public pgp key-server
|
||||
network. If you have automatic key-fetching enabled, just running a normal
|
||||
"gpg --verify" will grab my key automatically.
|
||||
Or, feel free to grab <a href="https://opencoder.net/WayneDavison.key">the gpp
|
||||
key for Wayne Davison</a> manually.<p>
|
||||
|
||||
From 3.4.0 and later releases will be signed by Andrew
|
||||
Tridgell. Please fetch the key for andrew@tridgell.net from https://keys.openpgp.org/
|
||||
|
||||
<h2>Binaries</h2>
|
||||
|
||||
<p>Precompiled binaries are available in most modern OS distributions, so
|
||||
you should first check if you can install an rsync package via your
|
||||
standard package-install tools for your OS.
|
||||
|
||||
<h3>Ubuntu</h3>
|
||||
|
||||
<p>The rsync project maintains a Launchpad PPA that tracks upstream stable
|
||||
releases for the currently supported Ubuntu series (jammy 22.04 LTS,
|
||||
noble 24.04 LTS, questing 25.10, resolute 26.04 LTS). This is the
|
||||
fastest way to get the latest upstream rsync on Ubuntu without waiting
|
||||
for the distro to update its packaged version:
|
||||
|
||||
<p><code><small>sudo add-apt-repository ppa:rsyncproject/rsync<br>
|
||||
sudo apt update && sudo apt install rsync</small></code>
|
||||
|
||||
<p>See the <a href="https://launchpad.net/~rsyncproject/+archive/ubuntu/rsync">PPA page on Launchpad</a>
|
||||
for build status across architectures.
|
||||
|
||||
<p>The <a href="https://github.com/RsyncProject/rsync/actions">GitHub Actions
|
||||
page</a> has build events that each generate a few binary artifact zip files
|
||||
(just click through via the build's title to see them). The actions page is
|
||||
also linked via the various green build-status icons on the web pages here.
|
||||
These builds use the newest libraries, such as xxhash checksums and zstd
|
||||
compression, and are dynamically linked, so you may need to install some
|
||||
official library packages for your distribution. If you're curious how the
|
||||
build was done, you can look at the build rules in the "Workflow file" tab.
|
||||
See the <a href="https://download.samba.org/pub/rsync/INSTALL">INSTALL.md</a>
|
||||
file for some package name hints, though you can use the non-devel versions of
|
||||
the various lib packages and ignore the gcc/autoconf/awk packages.
|
||||
|
||||
<p>There are also packages available from some <b>3rd-parties</b> (note that we
|
||||
cannot vouch for 3rd parties, so use a source that you trust):
|
||||
|
||||
<ul>
|
||||
|
||||
<li><p><a href="https://www.cygwin.com/">Cygwin</a> is a Posix runtime for MS
|
||||
Windows that includes rsync among their many packages.</p></li>
|
||||
|
||||
<li><p><a href="https://www.itefix.net/cwrsync">cwRsync</a> is a native
|
||||
packaging of rsync for MS Windows (they appear to only provide paid releases,
|
||||
though).</p></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>Git source vs Release files</h2>
|
||||
|
||||
The release tar files come with a few generated files that are not checked in to git.
|
||||
These mainly include the man pages and the configure related files. To make use of
|
||||
the git-derived files you will need autoconf, autoheader, and a version of python3
|
||||
that has the commonmark lib (OR cmarkgfm). If you have trouble with setting up the
|
||||
those required files, you can try running "./prepare-source fetchgen" to grab the
|
||||
very latest generated files that were created from the latest commit into the master
|
||||
branch.
|
||||
|
||||
<p> <b>Note:</b> Since the source repository is a work in progress it may, at
|
||||
times, not compile or fail in various ways, though it is usually pretty good.
|
||||
|
||||
<h2>Source repository patches</h2>
|
||||
|
||||
<p>There are also various patch files in the "rsync-patches" repository that
|
||||
represent either some work-in-progress features or features that are considered
|
||||
to be a little too fringe-interest for the main release. See the github link
|
||||
above for how to look around at what is available, or snag a release tar file.
|
||||
The maintainer like to put the rsync-patches dir into his rsync checkout as a
|
||||
directory named "patches" and has some helper scripts for how to use local git
|
||||
branches to test and update the diffs.
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
54
rsync-web/examples.html
Normal file
54
rsync-web/examples.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync examples</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">rsync examples</H2>
|
||||
|
||||
If you have an interesting example of how you use rsync then please
|
||||
submit it to the
|
||||
<A HREF="mailto:rsync-bugs@samba.org">rsync-bugs@samba.org</A>
|
||||
for inclusion on this page.
|
||||
|
||||
<h2>backup to a central backup server with 7 day incremental</h2>
|
||||
|
||||
<pre><small>
|
||||
<!--#include virtual="backup.txt" -->
|
||||
</small></pre>
|
||||
|
||||
<H2>backup to a spare disk</H2>
|
||||
|
||||
<pre><small>
|
||||
<!--#include virtual="horus.txt" -->
|
||||
</small></pre>
|
||||
|
||||
<H2>mirroring vger CVS tree</H2>
|
||||
|
||||
<pre><small>
|
||||
<!--#include virtual="vger.txt" -->
|
||||
</small></pre>
|
||||
|
||||
<H2>automated backup at home</H2>
|
||||
|
||||
<pre><small>
|
||||
<!--#include virtual="susan.txt" -->
|
||||
</small></pre>
|
||||
|
||||
<H2>Fancy footwork with remote file lists</H2>
|
||||
|
||||
<pre><small>
|
||||
One little known feature of rsync is the fact that when run over a
|
||||
remote shell (such as rsh or ssh) you can give any shell command as
|
||||
the remote file list. The shell command is expanded by your remote
|
||||
shell before rsync is called. For example, see if you can work out
|
||||
what this does:
|
||||
|
||||
rsync -avR remote:'`find /home -name "*.[ch]"`' /tmp/
|
||||
|
||||
note that that is backquotes enclosed by quotes (some browsers don't
|
||||
show that correctly).
|
||||
</small></pre>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
28
rsync-web/features.html
Normal file
28
rsync-web/features.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync features</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">rsync features</H2>
|
||||
|
||||
rsync is a file transfer program for Unix systems. rsync uses the
|
||||
"rsync algorithm" which provides a very fast method for bringing
|
||||
remote files into sync. It does this by sending just the differences
|
||||
in the files across the link, without requiring that both sets of
|
||||
files are present at one of the ends of the link beforehand. <p>
|
||||
|
||||
Some features of rsync include
|
||||
|
||||
<ul>
|
||||
<li> can update whole directory trees and filesystems
|
||||
<li> optionally preserves symbolic links, hard links, file ownership,
|
||||
permissions, devices and times
|
||||
<li> requires no special privileges to install
|
||||
<li> internal pipelining reduces latency for multiple files
|
||||
<li> can use rsh, ssh or direct sockets as the transport
|
||||
<li> supports <A HREF="http://dslab.lzu.edu.cn:8080/members/wangbj/wangbaojun/howtos/rsync-mirror-HOWTO/rsync-mirroring02.html">anonymous rsync</A> which is ideal for mirroring
|
||||
</ul>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
13
rsync-web/filt
Normal file
13
rsync-web/filt
Normal file
@@ -0,0 +1,13 @@
|
||||
P /badge.svg
|
||||
H /.git/
|
||||
H /.gitignore
|
||||
H /bin/
|
||||
H /upload/
|
||||
P /USE_THE_GIT_rsync-web_REPO_NOW
|
||||
H /filt
|
||||
H *.swp
|
||||
- /netware/
|
||||
- /pre-change
|
||||
- /rsync-and-debian/
|
||||
- /index.html-*
|
||||
- *~
|
||||
13
rsync-web/find.html
Normal file
13
rsync-web/find.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync search</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">Search the rsync web pages</H2>
|
||||
|
||||
<p>Searching samba.org will find results in the main web pages and in the
|
||||
archived mailing-list pages too.
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
189
rsync-web/firewall.html
Normal file
189
rsync-web/firewall.html
Normal file
@@ -0,0 +1,189 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync firewall tunneling</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">Using rsync through a firewall</H2>
|
||||
|
||||
<p>If you have a setup where there is no way to directly connect two
|
||||
systems for an rsync transfer, there are several ways to get a firewall
|
||||
system to act as an intermediary in the transfer.
|
||||
|
||||
<p>This first method should work for any remote-shell (e.g. ssh, rsh, etc).
|
||||
The other methods are all targeted at ssh (which has a lot of flexibility
|
||||
in making a tunneled connection possible).
|
||||
|
||||
<h4>Method 1 -- should work with any remote-shell</h4>
|
||||
|
||||
<p>Use your remote shell (e.g. ssh) to access the middle system and have it
|
||||
use a remote shell to hop over to the actual target system.
|
||||
|
||||
<p>To effect this extra hop, you'll need to make sure that the remote-shell
|
||||
connection from the middle system to the target system does not involve any
|
||||
tty-based user interaction (such as prompting for a password) because there
|
||||
is no way for the middle system to access the local user's tty.
|
||||
|
||||
<p>One way that should work for all remote-shell programs is to enable host-based
|
||||
authentication, which would allow all connections from the middle system to
|
||||
the target system to succeed (when the username remains the same).
|
||||
However, this may not be a desirable setup.
|
||||
|
||||
<p>A better method that works with ssh (and is very safe) is to setup an ssh
|
||||
key (see the ssh-keygen manpage) and ensure that ssh-agent forwarding is turned
|
||||
on in your ssh client config (e.g. "ForwardAgent yes"). You would put
|
||||
the public version of your key onto the middle and target systems (in the
|
||||
~/.ssh/authorized_keys file), and the private key on your local system (which
|
||||
I recommend you encrypt). With this setup, a series of ssh connections that
|
||||
starts from the system where your private key is available will auto-authorize
|
||||
(after a pass-phrase prompt on the first system if your key is encrypted).
|
||||
See also ssh-agent for a way to keep a public key unlocked in memory for an
|
||||
extended time, and the keychain project for a way to manage a system-wide,
|
||||
per-user ssh-agent.
|
||||
|
||||
<p>To test that you have things setup right, first test a series of
|
||||
remote-shell connections outside of rsync. A command such as the following
|
||||
should work without issuing multiple prompts (use the appropriate remote-shell
|
||||
and the substitute the real hostnames for "middle" and "target", of course):
|
||||
|
||||
<blockquote><pre>ssh middle ssh target uptime</pre></blockquote>
|
||||
|
||||
<p>If you get a password/passphrase prompt to get into the first ("middle")
|
||||
system that's fine, but the extra hop needs to occur without any extra user
|
||||
interaction.
|
||||
|
||||
<p>Once that's done, you can do an rsync copy like this:
|
||||
|
||||
<blockquote><pre>rsync -av -e "ssh middle ssh" target:/src/ /dest/</pre></blockquote>
|
||||
|
||||
<h4>Method 2 -- requires ssh and nc (netcat)</h4>
|
||||
|
||||
<p>Assuming you're using ssh as your remote shell, you can configure ssh to
|
||||
use a proxy command to get to the remote host you're interested in reaching.
|
||||
Doing this will allow the multi-hop connection to work with rsync, even if
|
||||
both hosts prompt for a password -- this is because both ssh connections
|
||||
originate from the localhost, and thus both instances of ssh have access to
|
||||
the local tty to use for an out-of-band password prompt.
|
||||
|
||||
<p>Here is an example config for your ~/.ssh/config file (substitute "target",
|
||||
"target_user", and "middle" as appropriate):
|
||||
|
||||
<blockquote><pre>Host target
|
||||
ProxyCommand nohup ssh middle nc -w1 %h %p
|
||||
User target_user
|
||||
</pre></blockquote>
|
||||
|
||||
<p>This proxy setup uses "ssh" to login to the firewall system ("middle") and
|
||||
uses "nc" (netcat) to connect to the target host ("target") using the default
|
||||
port number. The use of "nohup" silences a warning at the end of the run, and
|
||||
the "-w1" option tells nc to shut down when the connection closes.
|
||||
|
||||
<p>With this done, you can run a normal-looking rsync command to "target" that
|
||||
will run the proxy command to get through the firewall system:
|
||||
|
||||
<blockquote><pre>rsync -av /src/ target:/dest/</pre></blockquote>
|
||||
|
||||
<h4>Method 3 -- an alternate ssh method for those without nc (netcat)</h4>
|
||||
|
||||
<p>Assuming you're using ssh as your remote shell, you can configure ssh to
|
||||
forward a local port through your middle system to the ssh port (22) on the
|
||||
target system. This method does not require the use of nc (it uses only
|
||||
ssh to effect the extra hop), but otherwise it is similar to, but slightly
|
||||
less convenient than, method 2.
|
||||
|
||||
<p>The first thing we need is an ssh configuration that will allow us to
|
||||
connect to the forwarded port as if we were connecting to the target
|
||||
system, and we need ssh to know what we're doing so that it doesn't
|
||||
complain about the host keys being wrong. We can do this by adding this
|
||||
section to your ~/.ssh/config file (substitute "target" and "target_user"
|
||||
as appropriate, but leave "localhost" unchanged):
|
||||
|
||||
<blockquote><pre>Host target
|
||||
HostName localhost
|
||||
Port 2222
|
||||
HostKeyAlias target
|
||||
User target_user
|
||||
</pre></blockquote>
|
||||
|
||||
<p>Next, we need to enable the port forwarding:
|
||||
|
||||
<blockquote><pre>ssh -fN -l middle_user -L 2222:target:22 middle</pre></blockquote>
|
||||
|
||||
<p>What this does is cause a connection to port 2222 on the local system to
|
||||
get tunneled to the middle system and then turn into a connection to the
|
||||
target system's port 22. The -N option tells ssh not to start a shell on
|
||||
the remote system, which works with modern ssh versions (you can run a
|
||||
sleep command if -N doesn't work). The -f option tells ssh to put the
|
||||
command in the background after any password/passphrase prompts.
|
||||
|
||||
<p>With this done, you could run a normal-looking rsync command to "target"
|
||||
that would use a connection to port 2222 on localhost automatically:
|
||||
|
||||
<blockquote><pre>rsync -av target:/src/ /dest/</pre></blockquote>
|
||||
|
||||
<p><b>Note:</b> starting an ssh tunnel allows anyone on the source system
|
||||
to connect to the localhost port 2222, not just you, but they'd still need
|
||||
to be able to login to the target system using their own credentials.
|
||||
|
||||
<h4>Method 4 -- for using rsync in daemon-mode (requires nc)</h4>
|
||||
|
||||
<p>Install and configure an rsync daemon on the target and use ssh and nc
|
||||
to send the socket data to the remote host.
|
||||
|
||||
<blockquote><pre>RSYNC_CONNECT_PROG='ssh -l middle_user middle nc %H 873' \
|
||||
rsync daemonuser@target::module/src/ /dest/</pre></blockquote>
|
||||
|
||||
<p>(You can also export that variable into your environment if you want to
|
||||
perform a series of daemon rsync commands through the same middle host.)
|
||||
|
||||
<p>This command takes advantage of the RSYNC_CONNECT_PROG environment
|
||||
variable, which tells rsync to pipe its socket data to an external program
|
||||
in place of making a direct socket connection. The command specifed above
|
||||
uses ssh to run the nc (netcat) command on the middle host, which forwards
|
||||
all socket data to port 873 on the target host (%H). The "%H" will be
|
||||
substituted with the target host from the rsync command as long as you're
|
||||
running rsync 3.0.0 or newer (you'll need to replace %H with the actual
|
||||
target hostname for earlier rsync versions, which makes the hostname
|
||||
specified in the rsync command superfluous).
|
||||
|
||||
<h4>Method 5 -- for using rsync in daemon-mode (for those without nc)</h4>
|
||||
|
||||
<p>Install and configure an rsync daemon on the target and use an ssh
|
||||
tunnel to reach the rsync sever. This is similar to method 3, but it
|
||||
tunnels the daemon port for those that prefer to use an rsync daemon.
|
||||
|
||||
<p>(Note that this method also works to tunnel a socket connection
|
||||
directly to a destination system if you use "localhost" as the target
|
||||
hostname and your destination system's name as the middle hostname.)
|
||||
|
||||
<p>Installing the rsync daemon is beyond the scope of this document, but
|
||||
see the rsyncd.conf manpage for more information. Keep in mind that you
|
||||
don't need to be root to run an rsync daemon as long as you don't use a
|
||||
protected port.
|
||||
|
||||
<p>Once your rsync daemon is up and running, you build an ssh tunnel
|
||||
through your middle system like this:
|
||||
|
||||
<blockquote><pre>ssh -fN -l middle_user -L 8873:target:873 middle</pre></blockquote>
|
||||
|
||||
<p>What this does is cause a connection to port 8873 on the local system to
|
||||
turn into a connection from the middle system to the target system on port
|
||||
873. (Port 873 is the normal port for an rsync daemon.) The -N option
|
||||
tells ssh not to start a shell on the remote system, which works with
|
||||
modern ssh versions (you can run a sleep command if -N doesn't work). The
|
||||
-f option tells ssh to put the command in the background after any
|
||||
password/passphrase prompts.
|
||||
|
||||
<p>Now when an rsync command is executed with a daemon-mode command-line
|
||||
syntax to the local system, the conversation is directed to the target
|
||||
system. For example:
|
||||
|
||||
<blockquote><pre>rsync -av --port 8873 localhost::module/src/ dest/
|
||||
rsync -av rsync://localhost:8873/module/src/ dest/</pre></blockquote>
|
||||
|
||||
<p><b>Note:</b> starting an ssh tunnel allows anyone on the source system
|
||||
to connect to the localhost port 8873, not just you, so you may want to
|
||||
enable username/password restrictions on your rsync daemon.
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
19
rsync-web/footer.html
Normal file
19
rsync-web/footer.html
Normal file
@@ -0,0 +1,19 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<TR ALIGN="center">
|
||||
<TD><BR><a name="search"></a><img src="bar1.jpg" WIDTH="493" HEIGHT="26" BORDER="0" alt="=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=">
|
||||
|
||||
<!-- SiteSearch Google -->
|
||||
<form action="https://www.google.com/cse" id="cse-search-box"><div>
|
||||
<input type="hidden" name="cx" value="partner-pub-1444957896811922:vxjk7n-bst5" />
|
||||
<input type="hidden" name="ie" value="ISO-8859-1" />
|
||||
<input type="text" name="q" size="31" />
|
||||
<input type="submit" name="sa" value="Search" />
|
||||
</div></form>
|
||||
<script type="text/javascript" src="https://www.google.com/cse/brand?form=cse-search-box&lang=en"></script>
|
||||
<!-- SiteSearch Google -->
|
||||
|
||||
</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
40
rsync-web/header.html
Normal file
40
rsync-web/header.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<BODY BGCOLOR="#ffffff" TEXT="#000000"
|
||||
style="margin-top: 0">
|
||||
<TABLE BORDER=0 WIDTH="640" ALIGN="CENTER">
|
||||
<tr VALIGN="middle">
|
||||
<td ALIGN="left">
|
||||
<ul>
|
||||
<li><small><a href=".">home</a></small>
|
||||
<li><small><a href="FAQ.html">FAQ</a></small>
|
||||
<li><small><a href="resources.html">resources</a></small>
|
||||
<li><small><a href="features.html">features</a></small>
|
||||
<li><small><a href="examples.html">examples</a></small>
|
||||
<li><small><a href="lists.html">mailing lists</a></small>
|
||||
</ul>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="."><img src="newrsynclogo.jpg" border="0" alt="rsync"></a>
|
||||
</td>
|
||||
<td align="left">
|
||||
<ul>
|
||||
<li><small><a href="bug-tracking.html">bug-tracking</a></small>
|
||||
<li><small><a href="issues.html">current issues and debugging</a></small>
|
||||
<li><small><a href="download.html">download</a></small>
|
||||
<li><small><a href="documentation.html">documentation</a></small>
|
||||
<li><small><a href="find.html">search</a></small>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<TR ALIGN="center">
|
||||
<TD COLSPAN="3">
|
||||
<img src="bar1.jpg" WIDTH="493" HEIGHT="26"
|
||||
BORDER="0"
|
||||
alt="=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=">
|
||||
</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
<TABLE BORDER=0 WIDTH="640" ALIGN="CENTER">
|
||||
<tr VALIGN="middle">
|
||||
<td ALIGN="left">
|
||||
28
rsync-web/horus.txt
Normal file
28
rsync-web/horus.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
I do local backups on several of my machines using rsync. I have an
|
||||
extra disk installed that can hold all the contents of the main
|
||||
disk. I then have a nightly cron job that backs up the main disk to
|
||||
the backup. This is the script I use on one of those machines.
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
export PATH=/usr/local/bin:/usr/bin:/bin
|
||||
|
||||
LIST="rootfs usr data data2"
|
||||
|
||||
for d in $LIST; do
|
||||
mount /backup/$d
|
||||
rsync -ax --exclude fstab --delete /$d/ /backup/$d/
|
||||
umount /backup/$d
|
||||
done
|
||||
|
||||
DAY=`date "+%A"`
|
||||
|
||||
rsync -a --delete /usr/local/apache /data2/backups/$DAY
|
||||
rsync -a --delete /data/solid /data2/backups/$DAY
|
||||
|
||||
|
||||
|
||||
The first part does the backup on the spare disk. The second part
|
||||
backs up the critical parts to daily directories. I also backup the
|
||||
critical parts using a rsync over ssh to a remote machine.
|
||||
|
||||
350
rsync-web/how-rsync-works.html
Normal file
350
rsync-web/how-rsync-works.html
Normal file
@@ -0,0 +1,350 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>How Rsync Works</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">How Rsync Works<br>A Practical Overview</h1>
|
||||
<h2 align="center">Foreword</h2>
|
||||
<p>
|
||||
The original
|
||||
<a href="http://rsync.samba.org/tech_report/">Rsync technical report</a>
|
||||
and
|
||||
Andrew Tridgell's
|
||||
<a href="http://samba.org/%7Etridge/phd_thesis.pdf">Phd thesis (pdf)</a>
|
||||
Are both excellent documents for understanding the
|
||||
theoretical mathematics and some of the mechanics of the rsync algorithm.
|
||||
Unfortunately they are more about the theory than the
|
||||
implementation of the rsync utility (hereafter referred to as
|
||||
Rsync).
|
||||
<p>
|
||||
In this document I hope to describe...
|
||||
<ul>
|
||||
<li>A non-mathematical overview of the rsync algorithm.
|
||||
<li>How that algorithm is implemented in the rsync utility.
|
||||
<li>The protocol, in general terms, used by the rsync utility.
|
||||
<li>The identifiable roles the rsync processes play.
|
||||
</ul>
|
||||
<p>
|
||||
This document be able to serve as a guide for programmers
|
||||
needing something of an entré into the source code but the
|
||||
primary purpose is to give the reader a foundation from
|
||||
which he may understand
|
||||
<ul>
|
||||
<li>Why rsync behaves as it does.
|
||||
<li>The limitations of rsync.
|
||||
<li>Why a requested feature is unsuited to the code-base.
|
||||
</ul>
|
||||
<p>
|
||||
This document describes in general terms the construction
|
||||
and behaviour of Rsync. In some cases details and exceptions
|
||||
that would contribute to specific accuracy have
|
||||
been sacrificed for the sake meeting the broader goals.
|
||||
<h2 align="center">Processes and Roles</h2>
|
||||
<p>
|
||||
When we talk about Rsync we use specific terms to refer to
|
||||
various processes and their roles in the task performed by
|
||||
the utility. For effective communication it is important that we
|
||||
all be speaking the same language; likewise it is important
|
||||
that we mean the same things when we use certain terms in a
|
||||
given context. On the rsync mailing list there is often
|
||||
some confusion with regards to role and processes. For
|
||||
these reasons I will define a few terms
|
||||
used in the role and process contexts that will be used henceforth.
|
||||
|
||||
<table cellspacing="20"><tr valign="top">
|
||||
<td>client
|
||||
</td><td>role
|
||||
</td><td>
|
||||
The client initiates the synchronisation.
|
||||
</td></tr><tr valign="top">
|
||||
<td>server
|
||||
</td><td>role
|
||||
</td><td>
|
||||
The remote rsync process or system to which the
|
||||
client connects either within a local transfer, via
|
||||
a remote shell or via a network socket.
|
||||
<p>
|
||||
This is a general term and should not be confused with the daemon.
|
||||
</td></tr><tr valign="top">
|
||||
<td>
|
||||
</td><td>
|
||||
</td><td bgcolor="#dddddd">
|
||||
Once the connection between the client and server is established
|
||||
the distinction between them is superseded by the
|
||||
sender and receiver roles.
|
||||
</td></tr><tr valign="top">
|
||||
<td>daemon
|
||||
</td><td>Role and process
|
||||
</td><td>
|
||||
An Rsync process that awaits connections from
|
||||
clients. On a certain platform this would be called a
|
||||
service.
|
||||
</td></tr><tr valign="top">
|
||||
<td>remote shell
|
||||
</td><td>role and set of processes
|
||||
</td><td>
|
||||
One or more processes that provide connectivity
|
||||
between an Rsync client and an Rsync server on a
|
||||
remote system.
|
||||
</td></tr><tr valign="top">
|
||||
<td>sender
|
||||
</td><td>role and process
|
||||
</td><td>
|
||||
The Rsync process that has access to the source
|
||||
files being synchronised.
|
||||
</td></tr><tr valign="top">
|
||||
<td>receiver
|
||||
</td><td>role and process
|
||||
</td><td>
|
||||
As a role the receiver is the destination system.
|
||||
As a process the receiver is the process that
|
||||
receives update data and writes it to disk.
|
||||
</td></tr><tr valign="top">
|
||||
<td>generator
|
||||
</td><td>process
|
||||
</td><td>
|
||||
The generator process identifies changed files and
|
||||
manages the file level logic.
|
||||
</td></tr></table>
|
||||
<p>
|
||||
<h2 align="center">Process Startup</h2>
|
||||
<p>
|
||||
When an Rsync client is started it will first establish a
|
||||
connection with a server process. This connection may be
|
||||
through pipes or over a network socket.
|
||||
<p>
|
||||
When Rsync communicates with a remote non-daemon server via
|
||||
a remote shell the startup method is to fork the remote
|
||||
shell which will start an Rsync server on the remote system.
|
||||
Both the Rsync client and server are communicating via pipes
|
||||
through the remote shell. As far as the rsync processes are
|
||||
concerned there is no network.
|
||||
In this mode the rsync options for the server process are
|
||||
passed on the command-line that is used to start the remote
|
||||
shell.
|
||||
<p>
|
||||
When Rsync is communicating with a daemon it is
|
||||
communicating directly with a network socket. This is the
|
||||
only sort of Rsync communication that could be called
|
||||
network aware.
|
||||
In this mode the rsync options must be sent over the socket, as
|
||||
described below.
|
||||
<p>
|
||||
At the very start of the communication between the client
|
||||
and the server, they each send the maximum protocol version
|
||||
they support to the other side.
|
||||
Each side then uses the minimum value as the the protocol
|
||||
level for the transfer.
|
||||
If this is a daemon-mode connection, rsync options are sent
|
||||
from the client to the server. Then, the exclude list is
|
||||
transmitted. From this point onward the
|
||||
client-server relationship is relevant only with regards
|
||||
to error and log message delivery.
|
||||
<p>
|
||||
Local Rsync jobs (when the source and destination are both on locally
|
||||
mounted filesystems) are done exactly like a push. The
|
||||
client, which becomes the sender, forks a server process to
|
||||
fulfill the receiver role. The client/sender and
|
||||
server/receiver communicate with each other over pipes.
|
||||
<h2 align="center">The File List</h2>
|
||||
The file list includes not only the pathnames but also
|
||||
ownership, mode, permissions, size and modtime.
|
||||
If the --checksum option has been specified it also includes
|
||||
the file checksums.
|
||||
<p>
|
||||
The first thing that happens once the startup has completed
|
||||
is that the sender will create the file list.
|
||||
While it is being built, each entry is transmitted to the
|
||||
receiving side in a network-optimised way.
|
||||
<p>
|
||||
When this is done, each side sorts the file list lexicographically by path
|
||||
relative to the base directory of the transfer.
|
||||
(The exact sorting algorithm varies depending on what protocol
|
||||
version is in effect for the transfer.)
|
||||
Once that has happened all references to files
|
||||
will be done by their index in the file list.
|
||||
<p>
|
||||
If necessary the sender follows the file list with id→name
|
||||
tables for users and groups which the receiver will use to
|
||||
do a id→name→id translation for every file in the file
|
||||
list.
|
||||
<p>
|
||||
After the file list has been received by the receiver, it
|
||||
will fork to become the generator and receiver pair
|
||||
completing the pipeline.
|
||||
<h2 align="center">The Pipeline</h2>
|
||||
Rsync is heavily pipelined. This means that it is a set of
|
||||
processes that communicate in a (largely) unidirectional
|
||||
way. Once the file list has been shared the pipeline
|
||||
behaves like this:
|
||||
<blockquote>
|
||||
generator → sender → receiver
|
||||
</blockquote>
|
||||
<p>
|
||||
The output of the generator is input for the sender and the
|
||||
output of the sender is input for the receiver.
|
||||
Each process runs independently and is delayed only when the
|
||||
pipelines stall or when waiting for disk I/O or CPU resources.
|
||||
<h2 align="center">The Generator</h2>
|
||||
<p>
|
||||
The generator process compares the file list with its local
|
||||
directory tree. Prior to beginning its primary function, if
|
||||
--delete has been specified, it will first identify local
|
||||
files not on the sender and delete them on the receiver.
|
||||
<p>
|
||||
The generator will then start walking the file list. Each
|
||||
file will be checked to see if it can be skipped. In the
|
||||
most common mode of operation files are not skipped if the
|
||||
modification time or size differs. If --checksum was
|
||||
specified a file-level checksum will be created and
|
||||
compared. Directories, device nodes and symlinks are not
|
||||
skipped. Missing directories will be created.
|
||||
<p>
|
||||
If a file is not to be skipped, any existing version on the
|
||||
receiving side becomes the "basis file" for the transfer, and is
|
||||
used as a data source that will help to eliminate matching data
|
||||
from having to be sent by the sender. To effect this remote
|
||||
matching of data, block checksums are created for the basis file
|
||||
and sent to the sender immediately following the file's index
|
||||
number.
|
||||
An empty block checksum set is sent for new files and if
|
||||
--whole-file was specified.
|
||||
<p>
|
||||
The block size and, in later versions, the size of the
|
||||
block checksum are calculated on a per file basis according
|
||||
to the size of that file.
|
||||
<h2 align="center">The Sender</h2>
|
||||
The sender process reads the file index numbers and associated
|
||||
block checksum sets one at a time from the generator.
|
||||
<p>
|
||||
For each file id the generator sends it will store the
|
||||
block checksums and build a hash index of them for rapid lookup.
|
||||
<p>
|
||||
Then the local file is read and a checksum is
|
||||
generated for the block beginning with the first byte of the
|
||||
local file. This block checksum is looked for in the
|
||||
set that was sent by the generator, and if no match is found,
|
||||
the non-matching byte will be appended to the non-matching data
|
||||
and the block starting at the next byte will be compared.
|
||||
This is what
|
||||
is referred to as the “rolling checksum”
|
||||
<p>
|
||||
If a block checksum match is found it is considered a
|
||||
matching block and any accumulated non-matching data will be
|
||||
sent to the receiver followed by the offset and length in
|
||||
the receiver's file of the matching block and the block
|
||||
checksum generator will be advanced to the next byte after
|
||||
the matching block.
|
||||
<p>
|
||||
Matching blocks can be identified in this way even if
|
||||
the blocks are reordered or at different offsets.
|
||||
This process is the very heart of the rsync algorithm.
|
||||
<p>
|
||||
In this way, the sender will give the receiver instructions for
|
||||
how to reconstruct the source file into a new destination file.
|
||||
These instructions detail all the matching data that can be
|
||||
copied from the basis file (if one exists for the transfe),
|
||||
and includes any raw data that was not available locally.
|
||||
At the end of each file's processing a whole-file
|
||||
checksum is sent and the sender proceeds with the next
|
||||
file.
|
||||
<p>
|
||||
Generating the rolling checksums and searching for matches
|
||||
in the checksum set sent by the generator require a good
|
||||
deal of CPU power. Of all the rsync processes it is the
|
||||
sender that is the most CPU intensive.
|
||||
<h2 align="center">The Receiver</h2>
|
||||
<p>
|
||||
The receiver will read from the sender data for each file
|
||||
identified by the file index number. It will open the local
|
||||
file (called the basis) and will create a temporary file.
|
||||
<p>
|
||||
The receiver will expect to read non-matched data and/or to match
|
||||
records all in sequence for the final file contents. When
|
||||
non-matched data is read it will be written to the
|
||||
temp-file. When a block match record is received the
|
||||
receiver will seek to the block offset in the basis file
|
||||
and copy the block to the temp-file. In this way the
|
||||
temp-file is built from beginning to end.
|
||||
<p>
|
||||
The file's checksum is generated as the temp-file is built.
|
||||
At the end of the file, this checksum is compared with the
|
||||
file checksum from the sender. If the file checksums do not
|
||||
match the temp-file is deleted. If the file fails once it
|
||||
will be reprocessed in a second phase, and if it fails twice
|
||||
an error is reported.
|
||||
<p>
|
||||
After the temp-file has been completed, its ownership and
|
||||
permissions and modification time are set. It is then
|
||||
renamed to replace the basis file.
|
||||
<p>
|
||||
Copying data from the basis file to the temp-file make the
|
||||
receiver the most disk intensive of all the rsync processes.
|
||||
Small files may still be in disk cache mitigating this but
|
||||
for large files the cache may thrash as the generator has
|
||||
moved on to other files and there is further latency caused
|
||||
by the sender. As
|
||||
data is read possibly at random from one file and written to
|
||||
another, if the working set is larger than the disk cache,
|
||||
then what is called a seek storm can occur, further
|
||||
hurting performance.
|
||||
<h2 align="center">The Daemon</h2>
|
||||
The daemon process, like many daemons, forks for every
|
||||
connection. On startup, it parses the rsyncd.conf file
|
||||
to determine what modules exist and to set the global options.
|
||||
<p>
|
||||
When a connection is received for a defined module the
|
||||
daemon forks a new child process to handle the connection.
|
||||
That child process then reads the rsyncd.conf file to set
|
||||
the options for the requested module, which may chroot to the
|
||||
module path and may drop setuid and setgid for the
|
||||
process. After that it will behave just like any other
|
||||
rsync server process adopting either a sender or receiver
|
||||
role.
|
||||
<h2 align="center">The Rsync Protocol</h2>
|
||||
<p>
|
||||
A well-designed communications protocol has a number of
|
||||
characteristics.
|
||||
<ul>
|
||||
<li>Everything is sent in well defined packets with
|
||||
a header and an optional body or data payload.
|
||||
<li>In each packet's header a type and or command
|
||||
specified.
|
||||
<li>Each packet has a definite length.
|
||||
</ul>
|
||||
<p>
|
||||
In addition to these characteristics, protocols have varying degrees of
|
||||
statefulness, inter-packet independence, human readability,
|
||||
and the ability to reestablish a disconnected session.
|
||||
<p>
|
||||
Rsync's protocol has none of these good characteristics. The data is
|
||||
transferred as an unbroken stream of bytes. With the
|
||||
exception of the unmatched file-data, there are no length
|
||||
specifiers nor counts. Instead the meaning of each byte is
|
||||
dependent on its context as defined by the protocol level.
|
||||
<p>
|
||||
As an example, when the sender is sending the file list it
|
||||
simply sends each file list entry and terminates the list
|
||||
with a null byte. Within the file list entries, a bitfield
|
||||
indicates which fields of the structure to expect and those
|
||||
that are variable length strings are simply null terminated.
|
||||
The generator sending file numbers and block checksum sets
|
||||
works the same way.
|
||||
<p>
|
||||
This method of communication works quite well on reliable
|
||||
connections and it certainly has less data overhead than the
|
||||
formal protocols. It unfortunately makes the protocol
|
||||
extremely difficult to document, debug or extend.
|
||||
Each version of the protocol will have subtle differences on
|
||||
the wire that can only be anticipated by knowing the exact
|
||||
protocol version.
|
||||
<h2 align="center">notes</h2>
|
||||
This document is a work in progress. The author expects
|
||||
that it has some glaring oversights and some portions that may be
|
||||
more confusing than enlightening for some readers. It is
|
||||
hoped that this could evolve into a useful reference.
|
||||
<p>
|
||||
Specific suggestions for improvement are welcome, as would be a
|
||||
complete rewrite.
|
||||
</body>
|
||||
</html>
|
||||
528
rsync-web/index.html
Normal file
528
rsync-web/index.html
Normal file
@@ -0,0 +1,528 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync</TITLE>
|
||||
<style>
|
||||
.security { color: red; }
|
||||
h3 { margin-bottom: 0px; }
|
||||
.date { color: #D25A0B; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">Welcome to the rsync web pages</H2>
|
||||
|
||||
rsync is an <A HREF="https://www.opensource.org/">open source</A>
|
||||
utility that provides fast incremental file transfer. rsync is freely
|
||||
available under the <A HREF="GPL.html">GNU General Public
|
||||
License</A> and is currently being maintained by
|
||||
<a href="email:<tridge@samba.org>">Andrew Tridgell</a>.
|
||||
|
||||
<p>A full changelog of all the releases, including upcoming releases, is in the
|
||||
<a href="https://download.samba.org/pub/rsync/NEWS">NEWS file</a>.
|
||||
|
||||
<div style="float: right">
|
||||
<a href="https://github.com/RsyncProject/rsync/actions">
|
||||
<img src="badge.svg">
|
||||
</a></div>
|
||||
|
||||
<h3>Rsync version 3.4.3 released</h3>
|
||||
<i class=date>May 20th, 2026</i>
|
||||
|
||||
<p>Rsync version 3.4.3 has been released. This is a major security release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.4.3">3.4.3 NEWS</a> for a detailed changelog.
|
||||
The latest manpages are also available for:<ul>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsync.1"><b>rsync</b>(1)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsync-ssl.1"><b>rsync-ssl</b>(1)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsyncd.conf.5"><b>rsyncd.conf</b>(5)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rrsync.1"><b>rrsync</b>(1)</a>
|
||||
</ul>
|
||||
|
||||
<p>The source tar is available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-3.4.3.tar.gz">rsync-3.4.3.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-3.4.3.tar.gz.asc">signature</a>)</b>,
|
||||
and the diffs from version 3.4.2 are available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.4.2-3.4.3.diffs.gz">rsync-3.4.2-3.4.3.diffs.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.4.2-3.4.3.diffs.gz.asc">signature</a>)</b>.
|
||||
|
||||
<h3>Rsync version 3.4.2 released</h3>
|
||||
<i class=date>April 28th, 2026</i>
|
||||
|
||||
<p>Rsync version 3.4.2 has been released.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.4.2">3.4.2 NEWS</a> for a detailed changelog.
|
||||
The latest manpages are also available for:<ul>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsync.1"><b>rsync</b>(1)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsync-ssl.1"><b>rsync-ssl</b>(1)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsyncd.conf.5"><b>rsyncd.conf</b>(5)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rrsync.1"><b>rrsync</b>(1)</a>
|
||||
</ul>
|
||||
|
||||
<p>The source tar is available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-3.4.2.tar.gz">rsync-3.4.2.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-3.4.2.tar.gz.asc">signature</a>)</b>,
|
||||
and the diffs from version 3.4.1 are available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.4.1-3.4.2.diffs.gz">rsync-3.4.1-3.4.2.diffs.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.4.1-3.4.2.diffs.gz.asc">signature</a>)</b>.
|
||||
|
||||
<h3>Rsync version 3.4.1 released</h3>
|
||||
<i class=date>January 15th, 2025</i>
|
||||
|
||||
<p>Rsync version 3.4.1 has been released.
|
||||
This is a fix for some regressions in 3.4.0
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.4.1">3.4.1 NEWS</a> for a detailed changelog.
|
||||
The latest manpages are also available for:<ul>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsync.1"><b>rsync</b>(1)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsync-ssl.1"><b>rsync-ssl</b>(1)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rsyncd.conf.5"><b>rsyncd.conf</b>(5)</a>
|
||||
<li><a href="https://download.samba.org/pub/rsync/rrsync.1"><b>rrsync</b>(1)</a>
|
||||
</ul>
|
||||
|
||||
<p>The source tar is available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-3.4.1.tar.gz">rsync-3.4.1.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-3.4.1.tar.gz.asc">signature</a>)</b>,
|
||||
with a tar file of the "rsync-patches" repository released in a separate file:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.4.1.tar.gz">rsync-patches-3.4.1.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.4.1.tar.gz.asc">signature</a>)</b>,
|
||||
and the diffs from version 3.4.0 are available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.4.0-3.4.1.diffs.gz">rsync-3.4.0-3.4.1.diffs.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.4.0-3.4.1.diffs.gz.asc">signature</a>)</b>.
|
||||
|
||||
|
||||
<h3>Rsync version 3.4.0 released</h3>
|
||||
<i class=date>January 14th, 2025</i>
|
||||
|
||||
<p>Rsync version 3.4.0 has been released.
|
||||
This is a security release, fixing several important security vulnerabilities.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.4.0">3.4.0 NEWS</a> for a detailed changelog.
|
||||
|
||||
<p>The source tar is available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-3.4.0.tar.gz">rsync-3.4.0.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-3.4.0.tar.gz.asc">signature</a>)</b>,
|
||||
with a tar file of the "rsync-patches" repository released in a separate file:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.4.0.tar.gz">rsync-patches-3.4.0.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.4.0.tar.gz.asc">signature</a>)</b>,
|
||||
and the diffs from version 3.3.0 are available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.3.0-3.4.0.diffs.gz">rsync-3.3.0-3.4.0.diffs.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.3.0-3.4.0.diffs.gz.asc">signature</a>)</b>.
|
||||
|
||||
<h3>Rsync version 3.3.0 released</h3>
|
||||
<i class=date>April 6th, 2024</i>
|
||||
|
||||
<p>Rsync version 3.3.0 has been released.
|
||||
This is a bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.3.0">3.3.0 NEWS</a> for a detailed changelog.
|
||||
|
||||
<p>The source tar is available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-3.3.0.tar.gz">rsync-3.3.0.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-3.3.0.tar.gz.asc">signature</a>)</b>,
|
||||
with a tar file of the "rsync-patches" repository released in a separate file:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.3.0.tar.gz">rsync-patches-3.3.0.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.3.0.tar.gz.asc">signature</a>)</b>,
|
||||
and the diffs from version 3.2.2 are available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.2.7-3.3.0.diffs.gz">rsync-3.2.7-3.3.0.diffs.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.2.7-3.3.0.diffs.gz.asc">signature</a>)</b>.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.7 released</h3>
|
||||
<i class=date>October 20th, 2022</i>
|
||||
|
||||
<p>Rsync version 3.2.7 has been released.
|
||||
This has some new features & fixes, including various bug fixes for arg validation & filter-rule validation.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.7">3.2.7 NEWS</a> for a detailed changelog.
|
||||
|
||||
<p>The source tar is available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-3.2.7.tar.gz">rsync-3.2.7.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-3.2.7.tar.gz.asc">signature</a>)</b>,
|
||||
with a tar file of the "rsync-patches" repository released in a separate file:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.2.7.tar.gz">rsync-patches-3.2.7.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.2.7.tar.gz.asc">signature</a>)</b>,
|
||||
and the diffs from version 3.2.2 are available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.2.6-3.2.7.diffs.gz">rsync-3.2.6-3.2.7.diffs.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.2.6-3.2.7.diffs.gz.asc">signature</a>)</b>.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.6 released</h3>
|
||||
<i class=date>September 9th, 2022</i>
|
||||
|
||||
<p>Rsync version 3.2.6 has been released.
|
||||
This is a bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.6">3.2.6 NEWS</a> for a detailed changelog.
|
||||
|
||||
<p>The source tar is available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-3.2.6.tar.gz">rsync-3.2.6.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-3.2.6.tar.gz.asc">signature</a>)</b>,
|
||||
with a tar file of the "rsync-patches" repository released in a separate file:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.2.6.tar.gz">rsync-patches-3.2.6.tar.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src/rsync-patches-3.2.6.tar.gz.asc">signature</a>)</b>,
|
||||
and the diffs from version 3.2.2 are available here:
|
||||
<b><a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.2.5-3.2.6.diffs.gz">rsync-3.2.5-3.2.6.diffs.gz</a>
|
||||
(<a href="https://download.samba.org/pub/rsync/src-diffs/rsync-3.2.5-3.2.6.diffs.gz.asc">signature</a>)</b>.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.5 released</h3>
|
||||
<i class=date>August 14th, 2022</i>
|
||||
|
||||
<p>Rsync version 3.2.5 has been released.
|
||||
This is a bug-fix and <a href="security.html" class=security>security</a> release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.5">3.2.5 NEWS</a> for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.4 released</h3>
|
||||
<i class=date>April 15th, 2022</i>
|
||||
|
||||
<p>Rsync version 3.2.4 has been released.
|
||||
Another typical release with both bug fixes and some enhancements. It also contains a
|
||||
<a href="security.html#s3_2_4" class=security>security fix</a>
|
||||
for the bundled zlib 1.2.8, which may or may not be used in your particular build configuration.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.4">3.2.4 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.3 released</h3>
|
||||
<i class=date>August 6th, 2020</i>
|
||||
|
||||
<p>Rsync version 3.2.3 has been released.
|
||||
It contains a smattering of bug fixes and various enhancements.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.3">3.2.3 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.2 released</h3>
|
||||
<i class=date>July 4th, 2020</i>
|
||||
|
||||
<p>Rsync version 3.2.2 has been released.
|
||||
This is a few more portability fixes, some improvements to the newest features, and some
|
||||
other simple changes. Hopefully this will be the last of these recent touch-up releases.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.2">3.2.2 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.1 released</h3>
|
||||
<i class=date>June 22th, 2020</i>
|
||||
|
||||
<p>Rsync version 3.2.1 has been released.
|
||||
This is mainly a few fixes for some release issues and portability problems.
|
||||
There's also a couple new features, just for good measure.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.1">3.2.1 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.2.0 released</h3>
|
||||
<i class=date>June 19th, 2020</i>
|
||||
|
||||
<p>Rsync version 3.2.0 has been released.
|
||||
This release has a good number of a few new features and various bug fixes.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.2.0">3.2.0 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.1.3 released</h3>
|
||||
<i class=date>January 28th, 2018</i>
|
||||
|
||||
<p>Rsync version 3.1.3 has been released.
|
||||
This release has a
|
||||
<a href="security.html#s3_1_3" class=security>couple security fixes</a>,
|
||||
a few new features, and various bug fixes.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.1.3">3.1.3 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.1.2 released</h3>
|
||||
<i class=date>December 21st, 2015</i>
|
||||
|
||||
<p>Rsync version 3.1.2 has been released. This is a bug-fix release.
|
||||
It includes a <a href="security.html#s3_1_2" class=security>security fix</a>
|
||||
for a transfer from a sender that you don't fully trust.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.1.2">3.1.2 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.1.1 released</h3>
|
||||
<i class=date>June 22nd, 2014</i>
|
||||
|
||||
<p>Rsync version 3.1.1 has been released. This is a bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.1.1">3.1.1 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.1.0 released</h3>
|
||||
<i class=date>September 28th, 2013</i>
|
||||
|
||||
<p>Rsync version 3.1.0 has been released. This is a
|
||||
feature release that improves performance, provides several new options, and
|
||||
fixes a few bugs along the way.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.1.0">3.1.0 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.9 released</h3>
|
||||
<i class=date>September 23th, 2011</i>
|
||||
|
||||
<p>Rsync version 3.0.9 has been released. This is a bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.9">3.0.9 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.8 released</h3>
|
||||
<i class=date>March 26th, 2011</i>
|
||||
|
||||
<p>Rsync version 3.0.8 has been released. This is a bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.8">3.0.8 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.7 released</h3>
|
||||
<i class=date>December 31th, 2009</i>
|
||||
|
||||
<p>Rsync version 3.0.7 has been released. This is a bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.7">3.0.7 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.6 released</h3>
|
||||
<i class=date>May 8th, 2009</i>
|
||||
|
||||
<p>Rsync version 3.0.6 has been released. This is a bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.6">3.0.6 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.5 released</h3>
|
||||
<i class=date>December 28th, 2008</i>
|
||||
|
||||
<p>Rsync version 3.0.5 has been released. This is another bug-fix release.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.5">3.0.5 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.4 released</h3>
|
||||
<i class=date>September 6th, 2008</i>
|
||||
|
||||
<p>Rsync version 3.0.4 has been released. This is a bug-fix release with the
|
||||
only enhancement being the adding of a way to interact with an
|
||||
overly-restrictive server that refuses rsync's behind-the-scenes use of the -e
|
||||
option.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.4">3.0.4 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.3 released</h3>
|
||||
<i class=date>June 29th, 2008</i>
|
||||
|
||||
<p>Rsync version 3.0.3 has been released. This is a bug-fix release that has
|
||||
no new features (though it does have one new script in the support directory).
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.3">3.0.3 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.2 released</h3>
|
||||
<i class=date>April 8th, 2008</i>
|
||||
|
||||
<p>Rsync version 3.0.2 has been released. This is a
|
||||
<a href="security.html#s3_0_2" class=security>security release</a>
|
||||
that fixes a potential buffer-overflow issue.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.2">3.0.2 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.1 released</h3>
|
||||
<i class=date>April 3rd, 2008</i>
|
||||
|
||||
<p>Rsync version 3.0.1 has been released. This is a bug-fix release, which also
|
||||
includes fixes/improvements for several issues in the daemon-exclude code.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.1">3.0.1 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 3.0.0 released</h3>
|
||||
<i class=date>March 1st, 2008</i>
|
||||
|
||||
<p>Rsync version 3.0.0 is finally here! This is a feature release that
|
||||
also includes quite a few bug fixes.
|
||||
|
||||
<p>The 3.0.0 version number is such a large bump up from 2.6.9 due to the
|
||||
addition of an
|
||||
incremental recursion scan (which helps a lot with large transfers) and the
|
||||
official arrival of several other new features, including ACL support, extended
|
||||
attribute support, filename character-set conversion, etc.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#3.0.0">3.0.0 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 2.6.9 released</h3>
|
||||
<i class=date>November 6th, 2006</i>
|
||||
|
||||
<p>Rsync version 2.6.9 has been released. This is primarily a bug-fix
|
||||
release with a few minor new features.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.9">2.6.9 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync version 2.6.8 released</h3>
|
||||
<i class=date>April 22th, 2006</i>
|
||||
|
||||
<p>Rsync version 2.6.8 has been released. This is a bug-fix release that
|
||||
primarily addresses an exclude problem that affected the --relative option,
|
||||
but also includes a <a href="security.html#s2_6_8" class=security>security fix</a> for
|
||||
the xattrs.diff patch (which is not an
|
||||
official part of rsync, but some packagers include it in their release).
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.8">2.6.8 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.7 released</h3>
|
||||
<i class=date>March 11th, 2006</i>
|
||||
|
||||
<p>Rsync version 2.6.7 has been released. This release has both several new
|
||||
features and the usual accompaniment of bug fixes.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.7">2.6.7 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.6 released</h3>
|
||||
<i class=date>July 28th, 2005</i>
|
||||
|
||||
<p>Rsync version 2.6.6 has been released. This release is a bug-fix release
|
||||
which contains a <a href="security.html#s2_6_6" class=security>security fix</a>
|
||||
to handle a null-pointer bug that turned up in rsync's version of zlib
|
||||
1.1.4 (this is not the recent zlib 1.2.2 security fix, which did not
|
||||
affect rsync) and to squash a few other minor bugs. To deal with the
|
||||
zlib issue, rsync has been upgraded to include zlib 1.2.3.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.6">2.6.6 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.5 released</h3>
|
||||
<i class=date>June 1st, 2005</i>
|
||||
|
||||
<p>Rsync version 2.6.5 has been released. This release is primarily a bug-fix
|
||||
release to squash some annoying problems that made it into the (feature-filled)
|
||||
release of 2.6.4, plus a few minor enhancements.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.5">2.6.5 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.4 released</h3>
|
||||
<i class=date>March 30th, 2005</i>
|
||||
|
||||
<p>Rsync version 2.6.4 has been released. This release combines quite a
|
||||
few new features, some improved delete efficiency, and the usual array of
|
||||
bug fixes.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.4">2.6.4 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.3 released</h3>
|
||||
<i class=date>September 30th, 2004</i>
|
||||
|
||||
<p>Rsync version 2.6.3 has been released. It contains several new features
|
||||
and quite a few bug fixes, including a <a href="security.html#s2_6_3" class=security>security
|
||||
fix</a> for a patch-sanitizing bug in the daemon code.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.3">2.6.3 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.2 released</h3>
|
||||
<i class=date>April 30th, 2004</i>
|
||||
|
||||
<p>Rsync version 2.6.2 has been released. It is a bug-fix release that mainly
|
||||
fixes <b>a bug with the --relative option (-R) in 2.6.1</b>
|
||||
that could cause files to be transferred incorrectly. This only affected a
|
||||
source right at the root of the filesystem, such as "/" or "/*" (if you
|
||||
first "cd /" and then copy from ".", it would not tickle the bug).
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.2">2.6.2 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.1 released</h3>
|
||||
<i class=date>April 26th, 2004</i>
|
||||
|
||||
<p>Rsync version 2.6.1 has been released. It is primarily a performance
|
||||
release that requires less memory to run, makes fewer write calls to the socket
|
||||
(lowering the system CPU time), does less string copying (lowering the user CPU
|
||||
time), and also reduces the amount of data that is transmitted over the wire.
|
||||
There have also been quite a few bug fixes, including a
|
||||
<a href="security.html#s2_6_1" class=security>security fix</a> for a daemon problem when chroot
|
||||
is not enabled. See the
|
||||
<a href="https://download.samba.org/pub/rsync/NEWS#2.6.1">2.6.1 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<p><hr>
|
||||
<h3>One Cygwin hang-problem resolved</h3>
|
||||
|
||||
<p>The problem with rsync hanging at the end of the transfer on
|
||||
<a href="https://www.cygwin.com/">Cygwin</a> had been previously traced to a
|
||||
signal-handling bug in their compatibility DLL. This bug appears to now be
|
||||
fixed in DLL version 1.5.7-1, and Cygwin users are reporting that upgrading the
|
||||
DLL removes the hang-at-end-of-transfer problem for their existing rsync executable.
|
||||
(Note that this doesn't solve a hang that some folks see in the middle of a
|
||||
transfer -- using daemon mode instead of ssh can work around that one.)
|
||||
|
||||
<p><hr>
|
||||
<h3>Rsync 2.6.0 released</h3>
|
||||
<i class=date>January 1st, 2004</i>
|
||||
|
||||
<P> Two important things to note in the new release:
|
||||
|
||||
<ol>
|
||||
|
||||
<li>The default remote shell is now "ssh" unless you tell configure you want to
|
||||
make something else the default.
|
||||
|
||||
<li>Some bug fixes in the include/exclude code, while making things work
|
||||
properly, have resulted in some user-visible changes for certain wildcard
|
||||
strings. Read the BUG FIXES section in the
|
||||
<a href="https://download.samba.org/pub/rsync/NEWS#2.6.0">2.6.0 NEWS</a>
|
||||
to see if any of these changes apply to you.
|
||||
(Most people should be unaffected.)
|
||||
|
||||
</ol>
|
||||
|
||||
<p>One other item of note is that the oft-requested option "--files-from" is now
|
||||
available. This option lets you specify a list of files to transfer, and can
|
||||
be much more efficient than a recursive descent using include/exclude
|
||||
statements (if you know in advance what files you want to transfer). The list
|
||||
of files can come from either side of the connection, so it is possible for a
|
||||
server to provide the file-list that lets someone grab a server-specified set of
|
||||
files, for example. See the <a href="https://download.samba.org/pub/rsync/rsync.1">rsync man page</a>
|
||||
for more details.
|
||||
|
||||
<p>See the <a href="https://download.samba.org/pub/rsync/NEWS#2.6.0">2.6.0 NEWS</a>
|
||||
for a detailed changelog.
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
210
rsync-web/issues.html
Normal file
210
rsync-web/issues.html
Normal file
@@ -0,0 +1,210 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync current issues</TITLE>
|
||||
<style>
|
||||
code {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
white-space: pre;
|
||||
}
|
||||
pre code {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
blockquote pre code {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
</style>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">current issues and debugging</H2>
|
||||
|
||||
<ol>
|
||||
|
||||
<li><p><b>Q:</b>
|
||||
|
||||
Rsync appears hung -- what should I do?
|
||||
|
||||
<p><b>A:</b>
|
||||
|
||||
When experiencing a hang or freeze <b>please</b> gather the following
|
||||
information before killing the rsync process:
|
||||
|
||||
<ul>
|
||||
|
||||
<li> The state of the send/receive queues shown with netstat on the two ends.
|
||||
|
||||
<li> The system call that each of the 3 processes is stuck in (use truss on
|
||||
solaris, strace on Linux, etc.).
|
||||
|
||||
</ul>
|
||||
|
||||
<p>Try telling rsync on both sides of the connection to send messages to
|
||||
stderr, which might make the failure message visible. i.e., use:
|
||||
|
||||
<blockquote<pre><code>
|
||||
--msgs2stderr -M--msgs2stderr
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p>That alone might get rsync to stop hanging. Also, if you're using more than
|
||||
one <code>--verbose</code> (<code>-v</code>) option then I have 2 simple words
|
||||
for you: stop it. If you need more info on what rsync is changing, using the
|
||||
<code>--itemize-changes</code> option (<code>-i</code>) and repeat it if you
|
||||
need to see unchanged files. This is a much better way to go that doesn't fill
|
||||
up the communication pipeline with a large quanity of debug messages.
|
||||
|
||||
<p>See the "rsync-debug" script below for an example of how to grab strace
|
||||
information from the remote rsync process(es). If you need help, send email to
|
||||
the mailing list.
|
||||
|
||||
<li><p><b>Q:</b>
|
||||
|
||||
Why does my chrooted rsync daemon crash when doing an LDAP lookup for a user or
|
||||
group?
|
||||
|
||||
<p><b>A:</b>
|
||||
|
||||
There is a bug in some LDAP libraries (e.g. Fedora Core 3) where it crashes
|
||||
when someone looks up a name from inside a chrooted process (one that does not
|
||||
contain copies of the libraries to perform the lookup). This is a bug that the
|
||||
LDAP libraries will need to fix, and is out of rsync's hands. You can work
|
||||
around the problem by using the <code>--numeric-ids</code> option, turning
|
||||
chroot off, or getting rid of LDAP lookups.
|
||||
|
||||
<li><p><b>Q:</b>
|
||||
|
||||
Why does my transfer die with something like the following error?
|
||||
|
||||
<blockquote><pre><code>
|
||||
rsync: error writing 4 unbuffered bytes - exiting: Broken pipe
|
||||
rsync error: error in rsync protocol data stream (code 12) at io.c(463)
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p>or
|
||||
|
||||
<blockquote><pre><code>
|
||||
rsync: connection unexpectedly closed (24 bytes read so far)
|
||||
rsync error: error in rsync protocol data stream (code 12) at io.c(342)
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p><b>A:</b>
|
||||
|
||||
This error tells you that the local rsync was trying to talk to the remote
|
||||
rsync, but the connection to that rsync is now gone. The thing you must
|
||||
figure out is <b>why</b>, and that can involve some investigative work.
|
||||
|
||||
<p>It is a good idea use the <code>--msgs2stderr</code> options mentioned at
|
||||
the top of this page to get rsync to output any errors it encounters to stderr
|
||||
instead of trying to write them down the failing pipeline.
|
||||
|
||||
<p>If the connection is via ssh (or other remote-shell command) then you should
|
||||
run some tests to make sure that you can actually run the remote rsync and that
|
||||
your shell isn't injecting extraneous output into the rsync stream. For instance,
|
||||
try running these two commands using whatever HOST (and user) options you need:
|
||||
|
||||
<blockquote><pre><code>
|
||||
echo hi | ssh HOST cat
|
||||
ssh HOST rsync --version
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p>The first command should output just the string "hi" and nothing else. The
|
||||
second command should successfully start the remote rsync and report its version.
|
||||
|
||||
<p>If the remote rsync is a daemon, your first step should be to look at the
|
||||
daemon's log file to see if it logged an error explaining why it aborted the
|
||||
transfer. Also double-check to ensure that the log file is setup right, as a
|
||||
wrong "log file" setting in your rsyncd.conf file can also cause this problem.
|
||||
You could also halt the daemon and run it interactively using the
|
||||
<code>--no-detach</code> and <code>--msgs2stderr</code> options and look for
|
||||
errors while someone tries the rsync copy in another window.
|
||||
|
||||
<p>As for the cause of the remote rsync going away, there are several
|
||||
common issues that people run into:
|
||||
|
||||
<ul>
|
||||
|
||||
<li>The destination disk is full (remember that you need at least the
|
||||
size of the largest file that needs to be updated available in free
|
||||
disk space for the transfer to succeed).
|
||||
|
||||
<li>An idle connection caused a router or remote-shell server to close
|
||||
the connection.
|
||||
|
||||
<li>A network error caused the connection to be dropped.
|
||||
|
||||
<li>The remote rsync executable wasn't found.
|
||||
|
||||
<li>Your remote-shell setup isn't working right or isn't "clean"
|
||||
(i.e. it is sending spurious text to rsync).
|
||||
|
||||
</ul>
|
||||
|
||||
<p>If you think the problem might be an idle connection getting closed, you
|
||||
might be able to work around the problem by using a <code>--timeout</code>
|
||||
option (newer rsyncs send keep-alive messages during lulls). You can also
|
||||
configure ssh to send keep-alive messages when using Protocol 2 (look for
|
||||
KeepAlive, ServerAliveInterval, ClientAliveInterval, ServerAliveCountMax, and
|
||||
ClientAliveCountMax). You can also avoid some lulls by switching from
|
||||
<code>--delete</code> (aka <code>--delete-before</code>) to <code>--del</code>
|
||||
(aka <code>--delete-during</code>).
|
||||
|
||||
<p>If you can't figure out why the failure happened, there are steps
|
||||
you can take to debug the situation. One way is to create a shell
|
||||
script on the remote system such as
|
||||
<a href="rsync-debug">this one named "rsync-debug"</a>.
|
||||
You would use the script like this:
|
||||
|
||||
<blockquote><pre><code>
|
||||
rsync -av --rsync-path=/some/path/rsync-debug HOST:SOURCE DEST
|
||||
rsync -av --rsync-path=/some/path/rsync-debug SOURCE HOST:DEST
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p>This script enables core dumps and also logs all the OS system calls
|
||||
that lead up to the failure to a file in the /tmp dir. You can use the
|
||||
resulting files to help figure out why the remote rsync failed.
|
||||
|
||||
<p>If you are rsyncing directly to an rsync daemon (without using a
|
||||
remote-shell transport), the above script won't have
|
||||
any effect. Instead, halt the current daemon and run a debug version
|
||||
with core-dumps enabled and (if desired) using a
|
||||
system-call tracing utility such as <i>strace</i>, <i>truss</i>, or
|
||||
<i>tusc</i>. For strace, you would do it like this (the -f option
|
||||
tells strace to follow the child processes too):
|
||||
|
||||
<blockquote><pre><code>
|
||||
ulimit -c unlimited
|
||||
strace -f -t -s 1024 -o /tmp/rsync-$$.out rsync --daemon --no-detach
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p>Then, use a separate window to actually run the failing transfer, after
|
||||
which you can kill the debug rsync daemon (pressing Ctrl-C should do it).
|
||||
|
||||
<p>If you are using rsync under inetd, I'd suggest temporarily disabling
|
||||
that and using the above daemon approach to debug what is going on.
|
||||
|
||||
<li><p><b>Q:</b>
|
||||
|
||||
Why does my connection to an rsync daemon (using the "::" syntax)
|
||||
fail immediately with an error like the following?
|
||||
|
||||
<blockquote><pre><code>
|
||||
rsync: connection unexpectedly closed (24 bytes read so far)
|
||||
rsync error: error in rsync protocol data stream (code 12) at io.c(342)
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p><b>A:</b>
|
||||
|
||||
Older rsync daemons (before 2.6.3) were unable to return errors that were
|
||||
generated during the option-parsing phase of the transfer. Look in the
|
||||
logfile on the server to see if an error was reported, such as a "refused"
|
||||
option, an option that the server rsync doesn't support (e.g. perhaps
|
||||
links are not supported by the server), or some other failure (such as
|
||||
trying to send data to a read-only module). Upgrading the version of rsync
|
||||
that is running as a daemon to at least 2.6.3 will allow these errors to
|
||||
get returned to all rsync clients, old or new alike.
|
||||
|
||||
</ol>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
74
rsync-web/lists.html
Normal file
74
rsync-web/lists.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>rsync mailing lists</TITLE>
|
||||
</HEAD>
|
||||
<!--#include virtual="header.html" -->
|
||||
|
||||
<H2 align="center">rsync mailing lists</H2>
|
||||
|
||||
<p>
|
||||
There are three mailing lists for rsync. All are optionally
|
||||
available in digest mode and also through web archives.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Since these lists can generate a lot of traffic we suggest that
|
||||
you should not subscribe from a web-mail account such as Yahoo!
|
||||
or Hotmail, because your mailbox is likely to overflow.
|
||||
Instead, please read directly from the archives.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>rsync</b> is the main mailing lists for developers and
|
||||
users. It sees between zero and twenty messages per day.
|
||||
This is a good place to send questions about rsync, but please
|
||||
<a
|
||||
href="https://www.catb.org/~esr/faqs/smart-questions.html">read
|
||||
this before posting</a>.
|
||||
|
||||
[<a href="https://www.mail-archive.com/rsync@lists.samba.org/">archive 1</a>,
|
||||
<a href="https://lists.samba.org/archive/rsync/">archive 2</a>,
|
||||
<a href="https://lists.samba.org/mailman/listinfo/rsync">subscriptions</a>]
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<b>rsync-announce</b> carries only messages from the
|
||||
maintainer annoucing new releases, which happen at most a few
|
||||
times per month.
|
||||
|
||||
[<a href="https://www.mail-archive.com/rsync-announce@lists.samba.org/">archive 1</a>,
|
||||
<a href="https://lists.samba.org/archive/rsync-announce/">archive 2</a>,
|
||||
<a href="https://lists.samba.org/mailman/listinfo/rsync-announce">subscriptions</a>]
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<b>rsync-cvs</b> carries messages are automatically generated
|
||||
whenever a developer changes the source code, which can happen
|
||||
many times per day. If you subscribe, you should probably
|
||||
choose digest mode.
|
||||
|
||||
[<a href="https://www.mail-archive.com/rsync-cvs@lists.samba.org/maillist.html">archive 1</a>,
|
||||
<a href="https://lists.samba.org/archive/rsync-cvs/">archive 2</a>,
|
||||
<a href="https://lists.samba.org/mailman/listinfo/rsync-cvs">subscriptions</a>]
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Please report problems with the lists to the <tt>postmaster</tt>
|
||||
at <tt>samba.org</tt>, but note that you can control your own
|
||||
subscription using the web interface.
|
||||
</p>
|
||||
|
||||
<H2 align="center">rsync Discord server</H2>
|
||||
|
||||
<p>
|
||||
If you prefer real-time chat, there is also an rsync
|
||||
<a href="https://discord.gg/Avfvy9zhdp">Discord server</a> for
|
||||
discussion about rsync and its development.
|
||||
</p>
|
||||
|
||||
<!--#include virtual="footer.html" -->
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user