mirror of
https://github.com/borgbackup/borg.git
synced 2025-02-22 06:01:54 +00:00
FUSE: support pyfuse3 additionally to llfuse, fixes #5407
FUSE implementation can be switched via env var BORG_FUSE_IMPL.
This commit is contained in:
parent
d56c816cf2
commit
49b1421682
16 changed files with 215 additions and 126 deletions
38
.travis.yml
38
.travis.yml
|
@ -9,44 +9,40 @@ matrix:
|
||||||
include:
|
include:
|
||||||
- python: "3.6"
|
- python: "3.6"
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: bionic
|
||||||
env: TOXENV=py36
|
env: TOXENV=py36-fuse2
|
||||||
- python: "3.7"
|
- python: "3.7"
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: bionic
|
||||||
env: TOXENV=py37
|
env: TOXENV=py37-fuse2
|
||||||
- python: "3.7-dev"
|
|
||||||
os: linux
|
|
||||||
dist: xenial
|
|
||||||
env: TOXENV=py37
|
|
||||||
- python: "3.8"
|
- python: "3.8"
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: focal
|
||||||
env: TOXENV=py38
|
env: TOXENV=py38-fuse2
|
||||||
- python: "3.8-dev"
|
- python: "3.8-dev"
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: focal
|
||||||
env: TOXENV=py38
|
env: TOXENV=py38-fuse3
|
||||||
- python: "3.9-dev"
|
|
||||||
os: linux
|
|
||||||
dist: xenial
|
|
||||||
env: TOXENV=py39
|
|
||||||
- python: "3.9-dev"
|
- python: "3.9-dev"
|
||||||
os: linux
|
os: linux
|
||||||
dist: focal
|
dist: focal
|
||||||
env: TOXENV=py39
|
env: TOXENV=py39-fuse2
|
||||||
- python: "3.6"
|
- python: "3.9-dev"
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: focal
|
||||||
|
env: TOXENV=py39-fuse3
|
||||||
|
- python: "3.8"
|
||||||
|
os: linux
|
||||||
|
dist: focal
|
||||||
env: TOXENV=flake8
|
env: TOXENV=flake8
|
||||||
- language: generic
|
- language: generic
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode8.3 # This is the latest working xcode image with osxfuse compatibility; later images come with an OS X version which doesn't allow kernel extensions
|
osx_image: xcode8.3 # This is the latest working xcode image with osxfuse compatibility; later images come with an OS X version which doesn't allow kernel extensions
|
||||||
env: TOXENV=py36
|
env: TOXENV=py36-fuse2
|
||||||
- language: generic
|
- language: generic
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode11.3
|
osx_image: xcode11.3
|
||||||
env: TOXENV=py37 SKIPFUSE=true
|
env: TOXENV=py37 # No FUSE testing, because recent versions of macOS don't allow kernel extensions of osxfuse.
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- os: osx # OS X builds often take too long and time out, even though tests don't actually fail
|
- os: osx # OS X builds often take too long and time out, even though tests don't actually fail
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,8 @@ then
|
||||||
#sudo apt-get install -y liblz4-dev # Too old on trusty and xenial, but might be useful in future versions
|
#sudo apt-get install -y liblz4-dev # Too old on trusty and xenial, but might be useful in future versions
|
||||||
#sudo apt-get install -y libzstd-dev # Too old on trusty and xenial, but might be useful in future versions
|
#sudo apt-get install -y libzstd-dev # Too old on trusty and xenial, but might be useful in future versions
|
||||||
sudo apt-get install -y libacl1-dev
|
sudo apt-get install -y libacl1-dev
|
||||||
sudo apt-get install -y libfuse-dev fuse # Required for Python llfuse module
|
sudo apt-get install -y libfuse-dev fuse || true # Required for Python llfuse module
|
||||||
|
sudo apt-get install -y libfuse3-dev fuse3 || true # Required for Python pyfuse3 module
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
@ -67,12 +68,3 @@ pip install -r requirements.d/development.txt
|
||||||
pip install codecov
|
pip install codecov
|
||||||
python setup.py --version
|
python setup.py --version
|
||||||
|
|
||||||
# Recent versions of OS X don't allow kernel extensions which makes the osxfuse tests fail; those versions are marked with SKIPFUSE=true in .travis.yml
|
|
||||||
if [ "${SKIPFUSE}" = "true" ]
|
|
||||||
then
|
|
||||||
truncate -s 0 requirements.d/fuse.txt
|
|
||||||
pip install -e .
|
|
||||||
else
|
|
||||||
pip install -e .[fuse]
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
46
Vagrantfile
vendored
46
Vagrantfile
vendored
|
@ -15,7 +15,9 @@ def packages_debianoid(user)
|
||||||
apt-get -y -qq update
|
apt-get -y -qq update
|
||||||
apt-get -y -qq dist-upgrade
|
apt-get -y -qq dist-upgrade
|
||||||
# for building borgbackup and dependencies:
|
# for building borgbackup and dependencies:
|
||||||
apt install -y libssl-dev libacl1-dev liblz4-dev libzstd-dev libfuse-dev fuse pkg-config
|
apt install -y libssl-dev libacl1-dev liblz4-dev libzstd-dev pkg-config
|
||||||
|
apt install -y libfuse-dev fuse || true
|
||||||
|
apt install -y libfuse3-dev fuse3 || true
|
||||||
usermod -a -G fuse #{user}
|
usermod -a -G fuse #{user}
|
||||||
chgrp fuse /dev/fuse
|
chgrp fuse /dev/fuse
|
||||||
chmod 666 /dev/fuse
|
chmod 666 /dev/fuse
|
||||||
|
@ -43,7 +45,9 @@ def packages_freebsd
|
||||||
# install all the (security and other) updates, base system
|
# install all the (security and other) updates, base system
|
||||||
freebsd-update --not-running-from-cron fetch install
|
freebsd-update --not-running-from-cron fetch install
|
||||||
# for building borgbackup and dependencies:
|
# for building borgbackup and dependencies:
|
||||||
pkg install -y liblz4 zstd fusefs-libs pkgconf
|
pkg install -y liblz4 zstd pkgconf
|
||||||
|
pkg install -y fusefs-libs || true
|
||||||
|
pkg install -y fusefs-libs3 || true
|
||||||
pkg install -y git bash # fakeroot causes lots of troubles on freebsd
|
pkg install -y git bash # fakeroot causes lots of troubles on freebsd
|
||||||
# for building python:
|
# for building python:
|
||||||
pkg install -y python37 py37-sqlite3 py37-virtualenv py37-pip
|
pkg install -y python37 py37-sqlite3 py37-virtualenv py37-pip
|
||||||
|
@ -160,7 +164,7 @@ def build_pyenv_venv(boxname)
|
||||||
end
|
end
|
||||||
|
|
||||||
def install_borg(fuse)
|
def install_borg(fuse)
|
||||||
script = <<-EOF
|
return <<-EOF
|
||||||
. ~/.bash_profile
|
. ~/.bash_profile
|
||||||
cd /vagrant/borg
|
cd /vagrant/borg
|
||||||
. borg-env/bin/activate
|
. borg-env/bin/activate
|
||||||
|
@ -168,20 +172,8 @@ def install_borg(fuse)
|
||||||
cd borg
|
cd borg
|
||||||
pip install -r requirements.d/development.txt
|
pip install -r requirements.d/development.txt
|
||||||
python setup.py clean
|
python setup.py clean
|
||||||
|
pip install -e .[#{fuse}]
|
||||||
EOF
|
EOF
|
||||||
if fuse
|
|
||||||
script += <<-EOF
|
|
||||||
# by using [fuse], setup.py can handle different FUSE requirements:
|
|
||||||
pip install -e .[fuse]
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
script += <<-EOF
|
|
||||||
pip install -e .
|
|
||||||
# do not install llfuse into the virtualenvs built by tox:
|
|
||||||
sed -i.bak '/fuse.txt/d' tox.ini
|
|
||||||
EOF
|
|
||||||
end
|
|
||||||
return script
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def install_pyinstaller()
|
def install_pyinstaller()
|
||||||
|
@ -221,10 +213,10 @@ def run_tests(boxname)
|
||||||
# otherwise: just use the system python
|
# otherwise: just use the system python
|
||||||
if which fakeroot 2> /dev/null; then
|
if which fakeroot 2> /dev/null; then
|
||||||
echo "Running tox WITH fakeroot -u"
|
echo "Running tox WITH fakeroot -u"
|
||||||
fakeroot -u tox --skip-missing-interpreters -e py36,py37,py38,py39
|
fakeroot -u tox --skip-missing-interpreters
|
||||||
else
|
else
|
||||||
echo "Running tox WITHOUT fakeroot -u"
|
echo "Running tox WITHOUT fakeroot -u"
|
||||||
tox --skip-missing-interpreters -e py36,py37,py38,py39
|
tox --skip-missing-interpreters
|
||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
|
@ -263,7 +255,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
||||||
b.vm.provision "packages debianoid", :type => :shell, :inline => packages_debianoid("vagrant")
|
b.vm.provision "packages debianoid", :type => :shell, :inline => packages_debianoid("vagrant")
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("focal64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("focal64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(true)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("llfuse")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("focal64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("focal64")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -275,7 +267,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
||||||
b.vm.provision "packages debianoid", :type => :shell, :inline => packages_debianoid("vagrant")
|
b.vm.provision "packages debianoid", :type => :shell, :inline => packages_debianoid("vagrant")
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("bionic64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("bionic64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(true)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("llfuse")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("bionic64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("bionic64")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -289,7 +281,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "install pyenv", :type => :shell, :privileged => false, :inline => install_pyenv("buster64")
|
b.vm.provision "install pyenv", :type => :shell, :privileged => false, :inline => install_pyenv("buster64")
|
||||||
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("buster64")
|
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("buster64")
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("buster64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("buster64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(true)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("llfuse")
|
||||||
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
||||||
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("buster64")
|
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("buster64")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("buster64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("buster64")
|
||||||
|
@ -305,7 +297,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "install pyenv", :type => :shell, :privileged => false, :inline => install_pyenv("stretch64")
|
b.vm.provision "install pyenv", :type => :shell, :privileged => false, :inline => install_pyenv("stretch64")
|
||||||
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("stretch64")
|
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("stretch64")
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("stretch64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("stretch64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(true)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("llfuse")
|
||||||
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
||||||
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("stretch64")
|
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("stretch64")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("stretch64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("stretch64")
|
||||||
|
@ -319,7 +311,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
||||||
b.vm.provision "packages arch", :type => :shell, :privileged => true, :inline => packages_arch
|
b.vm.provision "packages arch", :type => :shell, :privileged => true, :inline => packages_arch
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("arch64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("arch64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(true)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("llfuse")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("arch64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("arch64")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -334,7 +326,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "install pyenv", :type => :shell, :privileged => false, :inline => install_pyenv("freebsd64")
|
b.vm.provision "install pyenv", :type => :shell, :privileged => false, :inline => install_pyenv("freebsd64")
|
||||||
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("freebsd64")
|
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("freebsd64")
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("freebsd64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("freebsd64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(true)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("llfuse")
|
||||||
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
||||||
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("freebsd64")
|
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("freebsd64")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("freebsd64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("freebsd64")
|
||||||
|
@ -349,7 +341,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
||||||
b.vm.provision "packages openbsd", :type => :shell, :inline => packages_openbsd
|
b.vm.provision "packages openbsd", :type => :shell, :inline => packages_openbsd
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("openbsd64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("openbsd64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(false)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("nofuse")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("openbsd64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("openbsd64")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -373,7 +365,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "fix pyenv", :type => :shell, :privileged => false, :inline => fix_pyenv_darwin("darwin64")
|
b.vm.provision "fix pyenv", :type => :shell, :privileged => false, :inline => fix_pyenv_darwin("darwin64")
|
||||||
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("darwin64")
|
b.vm.provision "install pythons", :type => :shell, :privileged => false, :inline => install_pythons("darwin64")
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("darwin64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_pyenv_venv("darwin64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(true)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("llfuse")
|
||||||
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
b.vm.provision "install pyinstaller", :type => :shell, :privileged => false, :inline => install_pyinstaller()
|
||||||
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("darwin64")
|
b.vm.provision "build binary with pyinstaller", :type => :shell, :privileged => false, :inline => build_binary_with_pyinstaller("darwin64")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("darwin64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("darwin64")
|
||||||
|
@ -389,7 +381,7 @@ Vagrant.configure(2) do |config|
|
||||||
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
b.vm.provision "fs init", :type => :shell, :inline => fs_init("vagrant")
|
||||||
b.vm.provision "packages openindiana", :type => :shell, :inline => packages_openindiana
|
b.vm.provision "packages openindiana", :type => :shell, :inline => packages_openindiana
|
||||||
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("openindiana64")
|
b.vm.provision "build env", :type => :shell, :privileged => false, :inline => build_sys_venv("openindiana64")
|
||||||
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg(false)
|
b.vm.provision "install borg", :type => :shell, :privileged => false, :inline => install_borg("nofuse")
|
||||||
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("openindiana64")
|
b.vm.provision "run tests", :type => :shell, :privileged => false, :inline => run_tests("openindiana64")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
# Ensure that the loggers exist for all tests
|
# Ensure that the loggers exist for all tests
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
||||||
from borg.testsuite import has_lchflags, has_llfuse
|
from borg.testsuite import has_lchflags, has_llfuse, has_pyfuse3
|
||||||
from borg.testsuite import are_symlinks_supported, are_hardlinks_supported, is_utime_fully_supported
|
from borg.testsuite import are_symlinks_supported, are_hardlinks_supported, is_utime_fully_supported
|
||||||
from borg.testsuite.platform import fakeroot_detected, are_acls_working
|
from borg.testsuite.platform import fakeroot_detected, are_acls_working
|
||||||
from borg import xattr
|
from borg import xattr
|
||||||
|
@ -33,7 +33,8 @@ def clean_env(tmpdir_factory, monkeypatch):
|
||||||
monkeypatch.setenv('XDG_CONFIG_HOME', str(tmpdir_factory.mktemp('xdg-config-home')))
|
monkeypatch.setenv('XDG_CONFIG_HOME', str(tmpdir_factory.mktemp('xdg-config-home')))
|
||||||
monkeypatch.setenv('XDG_CACHE_HOME', str(tmpdir_factory.mktemp('xdg-cache-home')))
|
monkeypatch.setenv('XDG_CACHE_HOME', str(tmpdir_factory.mktemp('xdg-cache-home')))
|
||||||
# also avoid to use anything from the outside environment:
|
# also avoid to use anything from the outside environment:
|
||||||
keys = [key for key in os.environ if key.startswith('BORG_')]
|
keys = [key for key in os.environ
|
||||||
|
if key.startswith('BORG_') and key not in ('BORG_FUSE_IMPL', )]
|
||||||
for key in keys:
|
for key in keys:
|
||||||
monkeypatch.delenv(key, raising=False)
|
monkeypatch.delenv(key, raising=False)
|
||||||
|
|
||||||
|
@ -41,7 +42,8 @@ def clean_env(tmpdir_factory, monkeypatch):
|
||||||
def pytest_report_header(config, startdir):
|
def pytest_report_header(config, startdir):
|
||||||
tests = {
|
tests = {
|
||||||
"BSD flags": has_lchflags,
|
"BSD flags": has_lchflags,
|
||||||
"fuse": has_llfuse,
|
"fuse2": has_llfuse,
|
||||||
|
"fuse3": has_pyfuse3,
|
||||||
"root": not fakeroot_detected(),
|
"root": not fakeroot_detected(),
|
||||||
"symlinks": are_symlinks_supported(),
|
"symlinks": are_symlinks_supported(),
|
||||||
"hardlinks": are_hardlinks_supported(),
|
"hardlinks": are_hardlinks_supported(),
|
||||||
|
|
|
@ -288,7 +288,7 @@ Usage::
|
||||||
Creating standalone binaries
|
Creating standalone binaries
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
Make sure you have everything built and installed (including llfuse and fuse).
|
Make sure you have everything built and installed (including fuse stuff).
|
||||||
When using the Vagrant VMs, pyinstaller will already be installed.
|
When using the Vagrant VMs, pyinstaller will already be installed.
|
||||||
|
|
||||||
With virtual env activated::
|
With virtual env activated::
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
.. _msgpack: https://msgpack.org/
|
.. _msgpack: https://msgpack.org/
|
||||||
.. _`msgpack-python`: https://pypi.python.org/pypi/msgpack-python/
|
.. _`msgpack-python`: https://pypi.python.org/pypi/msgpack-python/
|
||||||
.. _llfuse: https://pypi.python.org/pypi/llfuse/
|
.. _llfuse: https://pypi.python.org/pypi/llfuse/
|
||||||
|
.. _pyfuse3: https://pypi.python.org/pypi/pyfuse3/
|
||||||
.. _userspace filesystems: https://en.wikipedia.org/wiki/Filesystem_in_Userspace
|
.. _userspace filesystems: https://en.wikipedia.org/wiki/Filesystem_in_Userspace
|
||||||
.. _Cython: http://cython.org/
|
.. _Cython: http://cython.org/
|
||||||
.. _virtualenv: https://pypi.python.org/pypi/virtualenv/
|
.. _virtualenv: https://pypi.python.org/pypi/virtualenv/
|
||||||
|
|
|
@ -159,8 +159,12 @@ following dependencies first:
|
||||||
it will fall back to using the bundled code, see above).
|
it will fall back to using the bundled code, see above).
|
||||||
These must be present before invoking setup.py!
|
These must be present before invoking setup.py!
|
||||||
* some other Python dependencies, pip will automatically install them for you.
|
* some other Python dependencies, pip will automatically install them for you.
|
||||||
* optionally, the llfuse_ Python package is required if you wish to mount an
|
* optionally, if you wish to mount an archive as a FUSE filesystem, you need
|
||||||
archive as a FUSE filesystem. See setup.py about the version requirements.
|
a FUSE implementation for Python:
|
||||||
|
|
||||||
|
- Either pyfuse3_ (preferably, newer and maintained) or llfuse_ (older,
|
||||||
|
unmaintained now). See also the BORG_FUSE_IMPL env variable.
|
||||||
|
- See setup.py about the version requirements.
|
||||||
|
|
||||||
If you have troubles finding the right package names, have a look at the
|
If you have troubles finding the right package names, have a look at the
|
||||||
distribution specific sections below or the Vagrantfile in the git repository,
|
distribution specific sections below or the Vagrantfile in the git repository,
|
||||||
|
@ -186,7 +190,8 @@ Install the dependencies with development headers::
|
||||||
liblz4-dev libzstd-dev \
|
liblz4-dev libzstd-dev \
|
||||||
build-essential \
|
build-essential \
|
||||||
pkg-config python3-pkgconfig
|
pkg-config python3-pkgconfig
|
||||||
sudo apt-get install libfuse-dev fuse # optional, for FUSE support
|
sudo apt-get install libfuse-dev fuse # needed for llfuse
|
||||||
|
sudo apt-get install libfuse3-dev fuse3 # needed for pyfuse3
|
||||||
|
|
||||||
In case you get complaints about permission denied on ``/etc/fuse.conf``: on
|
In case you get complaints about permission denied on ``/etc/fuse.conf``: on
|
||||||
Ubuntu this means your user is not in the ``fuse`` group. Add yourself to that
|
Ubuntu this means your user is not in the ``fuse`` group. Add yourself to that
|
||||||
|
@ -203,7 +208,8 @@ Install the dependencies with development headers::
|
||||||
lz4-devel libzstd-devel \
|
lz4-devel libzstd-devel \
|
||||||
pkgconf python3-pkgconfig
|
pkgconf python3-pkgconfig
|
||||||
sudo dnf install gcc gcc-c++ redhat-rpm-config
|
sudo dnf install gcc gcc-c++ redhat-rpm-config
|
||||||
sudo dnf install fuse-devel fuse # optional, for FUSE support
|
sudo dnf install fuse-devel fuse # needed for llfuse
|
||||||
|
sudo dnf install fuse3-devel fuse3 # needed for pyfuse3
|
||||||
|
|
||||||
openSUSE Tumbleweed / Leap
|
openSUSE Tumbleweed / Leap
|
||||||
++++++++++++++++++++++++++
|
++++++++++++++++++++++++++
|
||||||
|
@ -218,7 +224,8 @@ Alternatively, you can enumerate all build dependencies in the command line::
|
||||||
libacl-devel openssl-devel \
|
libacl-devel openssl-devel \
|
||||||
python3-Cython python3-Sphinx python3-msgpack-python \
|
python3-Cython python3-Sphinx python3-msgpack-python \
|
||||||
python3-pytest python3-setuptools python3-setuptools_scm \
|
python3-pytest python3-setuptools python3-setuptools_scm \
|
||||||
python3-sphinx_rtd_theme python3-llfuse gcc gcc-c++
|
python3-sphinx_rtd_theme gcc gcc-c++
|
||||||
|
sudo zypper install python3-llfuse # llfuse
|
||||||
|
|
||||||
Mac OS X
|
Mac OS X
|
||||||
++++++++
|
++++++++
|
||||||
|
@ -234,7 +241,7 @@ For FUSE support to mount the backup archives, you need at least version 3.0 of
|
||||||
FUSE for OS X, which is available via `github
|
FUSE for OS X, which is available via `github
|
||||||
<https://github.com/osxfuse/osxfuse/releases/latest>`__, or via Homebrew::
|
<https://github.com/osxfuse/osxfuse/releases/latest>`__, or via Homebrew::
|
||||||
|
|
||||||
brew cask install osxfuse
|
brew cask install osxfuse # needed for llfuse
|
||||||
|
|
||||||
|
|
||||||
FreeBSD
|
FreeBSD
|
||||||
|
@ -248,7 +255,7 @@ and commands to make FUSE work for using the mount command.
|
||||||
pkg install -y python3 pkgconf
|
pkg install -y python3 pkgconf
|
||||||
pkg install openssl
|
pkg install openssl
|
||||||
pkg install liblz4 zstd
|
pkg install liblz4 zstd
|
||||||
pkg install fusefs-libs
|
pkg install fusefs-libs # needed for llfuse
|
||||||
pkg install -y git
|
pkg install -y git
|
||||||
python3.5 -m ensurepip # to install pip for Python3
|
python3.5 -m ensurepip # to install pip for Python3
|
||||||
To use the mount command:
|
To use the mount command:
|
||||||
|
@ -308,15 +315,17 @@ This will use ``pip`` to install the latest release from PyPi::
|
||||||
|
|
||||||
# might be required if your tools are outdated
|
# might be required if your tools are outdated
|
||||||
pip install -U pip setuptools wheel
|
pip install -U pip setuptools wheel
|
||||||
|
|
||||||
# install Borg + Python dependencies into virtualenv
|
# install Borg + Python dependencies into virtualenv
|
||||||
pip install borgbackup
|
pip install borgbackup
|
||||||
# or alternatively (if you want FUSE support):
|
# or alternatively (if you want FUSE support):
|
||||||
pip install borgbackup[fuse]
|
pip install borgbackup[llfuse] # to use llfuse
|
||||||
|
pip install borgbackup[pyfuse3] # to use pyfuse3
|
||||||
|
|
||||||
To upgrade Borg to a new version later, run the following after
|
To upgrade Borg to a new version later, run the following after
|
||||||
activating your virtual environment::
|
activating your virtual environment::
|
||||||
|
|
||||||
pip install -U borgbackup # or ... borgbackup[fuse]
|
pip install -U borgbackup # or ... borgbackup[llfuse/pyfuse3]
|
||||||
|
|
||||||
.. _git-installation:
|
.. _git-installation:
|
||||||
|
|
||||||
|
@ -339,8 +348,12 @@ While we try not to break master, there are no guarantees on anything.
|
||||||
cd borg
|
cd borg
|
||||||
pip install -r requirements.d/development.txt
|
pip install -r requirements.d/development.txt
|
||||||
pip install -r requirements.d/docs.txt # optional, to build the docs
|
pip install -r requirements.d/docs.txt # optional, to build the docs
|
||||||
pip install -r requirements.d/fuse.txt # optional, for FUSE support
|
|
||||||
pip install -e . # in-place editable mode
|
pip install -e . # in-place editable mode
|
||||||
|
or
|
||||||
|
pip install -e .[pyfuse3] # in-place editable mode, use pyfuse3
|
||||||
|
or
|
||||||
|
pip install -e .[llfuse] # in-place editable mode, use llfuse
|
||||||
|
|
||||||
# optional: run all the tests, on all supported Python versions
|
# optional: run all the tests, on all supported Python versions
|
||||||
# requires fakeroot, available through your package manager
|
# requires fakeroot, available through your package manager
|
||||||
|
|
|
@ -64,6 +64,16 @@ General:
|
||||||
When set to no (default: yes), system information (like OS, Python version, ...) in
|
When set to no (default: yes), system information (like OS, Python version, ...) in
|
||||||
exceptions is not shown.
|
exceptions is not shown.
|
||||||
Please only use for good reasons as it makes issues harder to analyze.
|
Please only use for good reasons as it makes issues harder to analyze.
|
||||||
|
BORG_FUSE_IMPL
|
||||||
|
Choose the lowlevel FUSE implementation borg shall use for ``borg mount``.
|
||||||
|
This is a comma-separated list of implementation names, they are tried in the
|
||||||
|
given order, e.g.:
|
||||||
|
|
||||||
|
- ``pyfuse3,llfuse``: default, first try to load pyfuse3, then try to load llfuse.
|
||||||
|
- ``llfuse,pyfuse3``: first try to load llfuse, then try to load pyfuse3.
|
||||||
|
- ``pyfuse3``: only try to load pyfuse3
|
||||||
|
- ``llfuse``: only try to load llfuse
|
||||||
|
- ``none``: do not try to load an implementation
|
||||||
BORG_WORKAROUNDS
|
BORG_WORKAROUNDS
|
||||||
A list of comma separated strings that trigger workarounds in borg,
|
A list of comma separated strings that trigger workarounds in borg,
|
||||||
e.g. to work around bugs in other software.
|
e.g. to work around bugs in other software.
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
# low-level FUSE support library for "borg mount"
|
|
||||||
# please see the comments in setup.py about llfuse.
|
|
||||||
llfuse >=1.3.4, <1.3.7; python_version <"3.9" # broken on py39
|
|
||||||
llfuse >=1.3.7, <2.0; python_version >="3.9" # broken on freebsd
|
|
15
setup.py
15
setup.py
|
@ -78,14 +78,17 @@
|
||||||
]
|
]
|
||||||
|
|
||||||
# note for package maintainers: if you package borgbackup for distribution,
|
# note for package maintainers: if you package borgbackup for distribution,
|
||||||
# please add llfuse as a *requirement* on all platforms that have a working
|
# please (if available) add pyfuse3 (preferably) or llfuse (not maintained any more)
|
||||||
# llfuse package. "borg mount" needs llfuse to work.
|
# as a *requirement*. "borg mount" needs one of them to work.
|
||||||
# if you do not have llfuse, do not require it, most of borgbackup will work.
|
# if neither is available, do not require it, most of borgbackup will work.
|
||||||
extras_require = {
|
extras_require = {
|
||||||
'fuse': [
|
'llfuse': [
|
||||||
'llfuse >=1.3.4, <1.3.7; python_version <"3.9"', # broken on py39
|
'llfuse >= 1.3.8',
|
||||||
'llfuse >=1.3.7, <2.0; python_version >="3.9"', # broken on freebsd
|
|
||||||
],
|
],
|
||||||
|
'pyfuse3': [
|
||||||
|
'pyfuse3 >= 3.1.1',
|
||||||
|
],
|
||||||
|
'nofuse': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
compress_source = 'src/borg/compress.pyx'
|
compress_source = 'src/borg/compress.pyx'
|
||||||
|
|
|
@ -1258,10 +1258,9 @@ def do_mount(self, args):
|
||||||
"""Mount archive or an entire repository as a FUSE filesystem"""
|
"""Mount archive or an entire repository as a FUSE filesystem"""
|
||||||
# Perform these checks before opening the repository and asking for a passphrase.
|
# Perform these checks before opening the repository and asking for a passphrase.
|
||||||
|
|
||||||
try:
|
from .fuse_impl import llfuse, BORG_FUSE_IMPL
|
||||||
import borg.fuse
|
if llfuse is None:
|
||||||
except ImportError as e:
|
self.print_error('borg mount not available: no FUSE support, BORG_FUSE_IMPL=%s.' % BORG_FUSE_IMPL)
|
||||||
self.print_error('borg mount not available: loading FUSE support failed [ImportError: %s]' % str(e))
|
|
||||||
return self.exit_code
|
return self.exit_code
|
||||||
|
|
||||||
if not os.path.isdir(args.mountpoint) or not os.access(args.mountpoint, os.R_OK | os.W_OK | os.X_OK):
|
if not os.path.isdir(args.mountpoint) or not os.access(args.mountpoint, os.R_OK | os.W_OK | os.X_OK):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import errno
|
import errno
|
||||||
|
import functools
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
|
@ -9,7 +10,23 @@
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from signal import SIGINT
|
from signal import SIGINT
|
||||||
|
|
||||||
import llfuse
|
from .fuse_impl import llfuse, has_pyfuse3
|
||||||
|
|
||||||
|
|
||||||
|
if has_pyfuse3:
|
||||||
|
import trio
|
||||||
|
|
||||||
|
def async_wrapper(fn):
|
||||||
|
@functools.wraps(fn)
|
||||||
|
async def wrapper(*args, **kwargs):
|
||||||
|
return fn(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
else:
|
||||||
|
trio = None
|
||||||
|
|
||||||
|
def async_wrapper(fn):
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
from .logger import create_logger
|
from .logger import create_logger
|
||||||
logger = create_logger()
|
logger = create_logger()
|
||||||
|
@ -26,7 +43,15 @@
|
||||||
|
|
||||||
|
|
||||||
def fuse_main():
|
def fuse_main():
|
||||||
return llfuse.main(workers=1)
|
if has_pyfuse3:
|
||||||
|
try:
|
||||||
|
trio.run(llfuse.main)
|
||||||
|
except:
|
||||||
|
return 1 # TODO return signal number if it was killed by signal
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return llfuse.main(workers=1)
|
||||||
|
|
||||||
|
|
||||||
# size of some LRUCaches (1 element per simultaneously open file)
|
# size of some LRUCaches (1 element per simultaneously open file)
|
||||||
|
@ -533,6 +558,7 @@ def pop_option(options, key, present, not_present, wanted_type, int_base=0):
|
||||||
finally:
|
finally:
|
||||||
llfuse.close(umount)
|
llfuse.close(umount)
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def statfs(self, ctx=None):
|
def statfs(self, ctx=None):
|
||||||
stat_ = llfuse.StatvfsData()
|
stat_ = llfuse.StatvfsData()
|
||||||
stat_.f_bsize = 512
|
stat_.f_bsize = 512
|
||||||
|
@ -546,7 +572,7 @@ def statfs(self, ctx=None):
|
||||||
stat_.f_namemax = 255 # == NAME_MAX (depends on archive source OS / FS)
|
stat_.f_namemax = 255 # == NAME_MAX (depends on archive source OS / FS)
|
||||||
return stat_
|
return stat_
|
||||||
|
|
||||||
def getattr(self, inode, ctx=None):
|
def _getattr(self, inode, ctx=None):
|
||||||
item = self.get_item(inode)
|
item = self.get_item(inode)
|
||||||
entry = llfuse.EntryAttributes()
|
entry = llfuse.EntryAttributes()
|
||||||
entry.st_ino = inode
|
entry.st_ino = inode
|
||||||
|
@ -568,10 +594,16 @@ def getattr(self, inode, ctx=None):
|
||||||
entry.st_birthtime_ns = item.get('birthtime', mtime_ns)
|
entry.st_birthtime_ns = item.get('birthtime', mtime_ns)
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
|
def getattr(self, inode, ctx=None):
|
||||||
|
return self._getattr(inode, ctx=ctx)
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def listxattr(self, inode, ctx=None):
|
def listxattr(self, inode, ctx=None):
|
||||||
item = self.get_item(inode)
|
item = self.get_item(inode)
|
||||||
return item.get('xattrs', {}).keys()
|
return item.get('xattrs', {}).keys()
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def getxattr(self, inode, name, ctx=None):
|
def getxattr(self, inode, name, ctx=None):
|
||||||
item = self.get_item(inode)
|
item = self.get_item(inode)
|
||||||
try:
|
try:
|
||||||
|
@ -579,6 +611,7 @@ def getxattr(self, inode, name, ctx=None):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise llfuse.FUSEError(llfuse.ENOATTR) from None
|
raise llfuse.FUSEError(llfuse.ENOATTR) from None
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def lookup(self, parent_inode, name, ctx=None):
|
def lookup(self, parent_inode, name, ctx=None):
|
||||||
self.check_pending_archive(parent_inode)
|
self.check_pending_archive(parent_inode)
|
||||||
if name == b'.':
|
if name == b'.':
|
||||||
|
@ -589,8 +622,9 @@ def lookup(self, parent_inode, name, ctx=None):
|
||||||
inode = self.contents[parent_inode].get(name)
|
inode = self.contents[parent_inode].get(name)
|
||||||
if not inode:
|
if not inode:
|
||||||
raise llfuse.FUSEError(errno.ENOENT)
|
raise llfuse.FUSEError(errno.ENOENT)
|
||||||
return self.getattr(inode)
|
return self._getattr(inode)
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def open(self, inode, flags, ctx=None):
|
def open(self, inode, flags, ctx=None):
|
||||||
if not self.allow_damaged_files:
|
if not self.allow_damaged_files:
|
||||||
item = self.get_item(inode)
|
item = self.get_item(inode)
|
||||||
|
@ -601,12 +635,14 @@ def open(self, inode, flags, ctx=None):
|
||||||
logger.warning('File has damaged (all-zero) chunks. Try running borg check --repair. '
|
logger.warning('File has damaged (all-zero) chunks. Try running borg check --repair. '
|
||||||
'Mount with allow_damaged_files to read damaged files.')
|
'Mount with allow_damaged_files to read damaged files.')
|
||||||
raise llfuse.FUSEError(errno.EIO)
|
raise llfuse.FUSEError(errno.EIO)
|
||||||
return inode
|
return llfuse.FileInfo(fh=inode) if has_pyfuse3 else inode
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def opendir(self, inode, ctx=None):
|
def opendir(self, inode, ctx=None):
|
||||||
self.check_pending_archive(inode)
|
self.check_pending_archive(inode)
|
||||||
return inode
|
return inode
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def read(self, fh, offset, size):
|
def read(self, fh, offset, size):
|
||||||
parts = []
|
parts = []
|
||||||
item = self.get_item(fh)
|
item = self.get_item(fh)
|
||||||
|
@ -650,12 +686,25 @@ def read(self, fh, offset, size):
|
||||||
break
|
break
|
||||||
return b''.join(parts)
|
return b''.join(parts)
|
||||||
|
|
||||||
def readdir(self, fh, off):
|
# note: we can't have a generator (with yield) and not a generator (async) in the same method
|
||||||
entries = [(b'.', fh), (b'..', self.parent[fh])]
|
if has_pyfuse3:
|
||||||
entries.extend(self.contents[fh].items())
|
async def readdir(self, fh, off, token):
|
||||||
for i, (name, inode) in enumerate(entries[off:], off):
|
entries = [(b'.', fh), (b'..', self.parent[fh])]
|
||||||
yield name, self.getattr(inode), i + 1
|
entries.extend(self.contents[fh].items())
|
||||||
|
for i, (name, inode) in enumerate(entries[off:], off):
|
||||||
|
attrs = self._getattr(inode)
|
||||||
|
if not llfuse.readdir_reply(token, name, attrs, i + 1):
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
def readdir(self, fh, off):
|
||||||
|
entries = [(b'.', fh), (b'..', self.parent[fh])]
|
||||||
|
entries.extend(self.contents[fh].items())
|
||||||
|
for i, (name, inode) in enumerate(entries[off:], off):
|
||||||
|
attrs = self._getattr(inode)
|
||||||
|
yield name, attrs, i + 1
|
||||||
|
|
||||||
|
@async_wrapper
|
||||||
def readlink(self, inode, ctx=None):
|
def readlink(self, inode, ctx=None):
|
||||||
item = self.get_item(inode)
|
item = self.get_item(inode)
|
||||||
return os.fsencode(item.source)
|
return os.fsencode(item.source)
|
||||||
|
|
36
src/borg/fuse_impl.py
Normal file
36
src/borg/fuse_impl.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
"""
|
||||||
|
load library for lowlevel FUSE implementation
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
BORG_FUSE_IMPL = os.environ.get('BORG_FUSE_IMPL', 'pyfuse3,llfuse')
|
||||||
|
|
||||||
|
for FUSE_IMPL in BORG_FUSE_IMPL.split(','):
|
||||||
|
FUSE_IMPL = FUSE_IMPL.strip()
|
||||||
|
if FUSE_IMPL == 'pyfuse3':
|
||||||
|
try:
|
||||||
|
import pyfuse3 as llfuse
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
has_llfuse = False
|
||||||
|
has_pyfuse3 = True
|
||||||
|
break
|
||||||
|
elif FUSE_IMPL == 'llfuse':
|
||||||
|
try:
|
||||||
|
import llfuse
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
has_llfuse = True
|
||||||
|
has_pyfuse3 = False
|
||||||
|
break
|
||||||
|
elif FUSE_IMPL == 'none':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise RuntimeError("unknown fuse implementation in BORG_FUSE_IMPL: '%s'" % BORG_FUSE_IMPL)
|
||||||
|
else:
|
||||||
|
llfuse = None
|
||||||
|
has_llfuse = False
|
||||||
|
has_pyfuse3 = False
|
|
@ -23,12 +23,10 @@
|
||||||
|
|
||||||
# Note: this is used by borg.selftest, do not use or import py.test functionality here.
|
# Note: this is used by borg.selftest, do not use or import py.test functionality here.
|
||||||
|
|
||||||
try:
|
from ..fuse_impl import llfuse, has_pyfuse3, has_llfuse
|
||||||
import llfuse
|
|
||||||
# Does this version of llfuse support ns precision?
|
# Does this version of llfuse support ns precision?
|
||||||
have_fuse_mtime_ns = hasattr(llfuse.EntryAttributes, 'st_mtime_ns')
|
have_fuse_mtime_ns = hasattr(llfuse.EntryAttributes, 'st_mtime_ns') if llfuse else False
|
||||||
except ImportError:
|
|
||||||
have_fuse_mtime_ns = False
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
|
@ -42,12 +40,6 @@
|
||||||
except OSError:
|
except OSError:
|
||||||
has_lchflags = False
|
has_lchflags = False
|
||||||
|
|
||||||
try:
|
|
||||||
import llfuse
|
|
||||||
has_llfuse = True or llfuse # avoids "unused import"
|
|
||||||
except ImportError:
|
|
||||||
has_llfuse = False
|
|
||||||
|
|
||||||
# The mtime get/set precision varies on different OS and Python versions
|
# The mtime get/set precision varies on different OS and Python versions
|
||||||
if posix and 'HAVE_FUTIMENS' in getattr(posix, '_have_functions', []):
|
if posix and 'HAVE_FUTIMENS' in getattr(posix, '_have_functions', []):
|
||||||
st_mtime_ns_round = 0
|
st_mtime_ns_round = 0
|
||||||
|
|
|
@ -26,11 +26,6 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
try:
|
|
||||||
import llfuse
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
import borg
|
import borg
|
||||||
from .. import xattr, helpers, platform
|
from .. import xattr, helpers, platform
|
||||||
from ..archive import Archive, ChunkBuffer
|
from ..archive import Archive, ChunkBuffer
|
||||||
|
@ -55,7 +50,7 @@
|
||||||
from ..logger import setup_logging
|
from ..logger import setup_logging
|
||||||
from ..remote import RemoteRepository, PathNotAllowed
|
from ..remote import RemoteRepository, PathNotAllowed
|
||||||
from ..repository import Repository
|
from ..repository import Repository
|
||||||
from . import has_lchflags, has_llfuse
|
from . import has_lchflags, llfuse
|
||||||
from . import BaseTestCase, changedir, environment_variable, no_selinux
|
from . import BaseTestCase, changedir, environment_variable, no_selinux
|
||||||
from . import are_symlinks_supported, are_hardlinks_supported, are_fifos_supported, is_utime_fully_supported, is_birthtime_fully_supported
|
from . import are_symlinks_supported, are_hardlinks_supported, are_fifos_supported, is_utime_fully_supported, is_birthtime_fully_supported
|
||||||
from .platform import fakeroot_detected
|
from .platform import fakeroot_detected
|
||||||
|
@ -798,7 +793,7 @@ def _extract_hardlinks_setup(self):
|
||||||
requires_hardlinks = pytest.mark.skipif(not are_hardlinks_supported(), reason='hardlinks not supported')
|
requires_hardlinks = pytest.mark.skipif(not are_hardlinks_supported(), reason='hardlinks not supported')
|
||||||
|
|
||||||
@requires_hardlinks
|
@requires_hardlinks
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_fuse_mount_hardlinks(self):
|
def test_fuse_mount_hardlinks(self):
|
||||||
self._extract_hardlinks_setup()
|
self._extract_hardlinks_setup()
|
||||||
mountpoint = os.path.join(self.tmpdir, 'mountpoint')
|
mountpoint = os.path.join(self.tmpdir, 'mountpoint')
|
||||||
|
@ -1661,7 +1656,7 @@ def test_readonly_list(self):
|
||||||
# verify that command works with read-only repo when using --bypass-lock
|
# verify that command works with read-only repo when using --bypass-lock
|
||||||
self.cmd('list', self.repository_location, '--bypass-lock')
|
self.cmd('list', self.repository_location, '--bypass-lock')
|
||||||
|
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_readonly_mount(self):
|
def test_readonly_mount(self):
|
||||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||||
self.create_src_archive('test')
|
self.create_src_archive('test')
|
||||||
|
@ -1754,7 +1749,7 @@ def test_unknown_feature_on_delete(self):
|
||||||
# delete of the whole repository ignores features
|
# delete of the whole repository ignores features
|
||||||
self.cmd('delete', self.repository_location)
|
self.cmd('delete', self.repository_location)
|
||||||
|
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_unknown_feature_on_mount(self):
|
def test_unknown_feature_on_mount(self):
|
||||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||||
self.cmd('create', self.repository_location + '::test', 'input')
|
self.cmd('create', self.repository_location + '::test', 'input')
|
||||||
|
@ -2322,7 +2317,7 @@ def test_help(self):
|
||||||
assert 'positional arguments' not in self.cmd('help', 'init', '--epilog-only')
|
assert 'positional arguments' not in self.cmd('help', 'init', '--epilog-only')
|
||||||
assert 'This command initializes' not in self.cmd('help', 'init', '--usage-only')
|
assert 'This command initializes' not in self.cmd('help', 'init', '--usage-only')
|
||||||
|
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_fuse(self):
|
def test_fuse(self):
|
||||||
def has_noatime(some_file):
|
def has_noatime(some_file):
|
||||||
atime_before = os.stat(some_file).st_atime_ns
|
atime_before = os.stat(some_file).st_atime_ns
|
||||||
|
@ -2423,7 +2418,7 @@ def has_noatime(some_file):
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_fuse_versions_view(self):
|
def test_fuse_versions_view(self):
|
||||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||||
self.create_regular_file('test', contents=b'first')
|
self.create_regular_file('test', contents=b'first')
|
||||||
|
@ -2455,7 +2450,7 @@ def test_fuse_versions_view(self):
|
||||||
assert os.stat(hl2).st_ino == os.stat(hl3).st_ino
|
assert os.stat(hl2).st_ino == os.stat(hl3).st_ino
|
||||||
assert open(hl3, 'rb').read() == b'123456'
|
assert open(hl3, 'rb').read() == b'123456'
|
||||||
|
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_fuse_allow_damaged_files(self):
|
def test_fuse_allow_damaged_files(self):
|
||||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||||
self.create_src_archive('archive')
|
self.create_src_archive('archive')
|
||||||
|
@ -2480,7 +2475,7 @@ def test_fuse_allow_damaged_files(self):
|
||||||
with self.fuse_mount(self.repository_location + '::archive', mountpoint, '-o', 'allow_damaged_files'):
|
with self.fuse_mount(self.repository_location + '::archive', mountpoint, '-o', 'allow_damaged_files'):
|
||||||
open(os.path.join(mountpoint, path)).close()
|
open(os.path.join(mountpoint, path)).close()
|
||||||
|
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_fuse_mount_options(self):
|
def test_fuse_mount_options(self):
|
||||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||||
self.create_src_archive('arch11')
|
self.create_src_archive('arch11')
|
||||||
|
@ -2502,7 +2497,7 @@ def test_fuse_mount_options(self):
|
||||||
with self.fuse_mount(self.repository_location, mountpoint, '--prefix=nope'):
|
with self.fuse_mount(self.repository_location, mountpoint, '--prefix=nope'):
|
||||||
assert sorted(os.listdir(os.path.join(mountpoint))) == []
|
assert sorted(os.listdir(os.path.join(mountpoint))) == []
|
||||||
|
|
||||||
@unittest.skipUnless(has_llfuse, 'llfuse not installed')
|
@unittest.skipUnless(llfuse, 'llfuse not installed')
|
||||||
def test_migrate_lock_alive(self):
|
def test_migrate_lock_alive(self):
|
||||||
"""Both old_id and new_id must not be stale during lock migration / daemonization."""
|
"""Both old_id and new_id must not be stale during lock migration / daemonization."""
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
19
tox.ini
19
tox.ini
|
@ -2,16 +2,29 @@
|
||||||
# fakeroot -u tox --recreate
|
# fakeroot -u tox --recreate
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py{36,37,38,39},flake8
|
envlist = py{36,37,38,39}-fuse{2,3}, flake8
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps =
|
deps =
|
||||||
-rrequirements.d/development.txt
|
-rrequirements.d/development.txt
|
||||||
-rrequirements.d/fuse.txt
|
|
||||||
commands = py.test -v -n {env:XDISTN:1} -rs --cov=borg --cov-config=.coveragerc --benchmark-skip --pyargs {posargs:borg.testsuite}
|
commands = py.test -v -n {env:XDISTN:1} -rs --cov=borg --cov-config=.coveragerc --benchmark-skip --pyargs {posargs:borg.testsuite}
|
||||||
# fakeroot -u needs some env vars:
|
# fakeroot -u needs some env vars:
|
||||||
passenv = *
|
passenv = *
|
||||||
|
|
||||||
|
[testenv:py{36,37,38,39}-fuse2]
|
||||||
|
setenv =
|
||||||
|
BORG_FUSE_IMPL=llfuse
|
||||||
|
deps =
|
||||||
|
llfuse
|
||||||
|
{[testenv]deps}
|
||||||
|
|
||||||
|
[testenv:py{36,37,38,39}-fuse3]
|
||||||
|
setenv =
|
||||||
|
BORG_FUSE_IMPL=pyfuse3
|
||||||
|
deps =
|
||||||
|
pyfuse3
|
||||||
|
{[testenv]deps}
|
||||||
|
|
||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
changedir =
|
changedir =
|
||||||
deps =
|
deps =
|
||||||
|
|
Loading…
Reference in a new issue