././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.7409198 borgbackup-2.0.0b14/0000755000076500000240000000000014716232130012513 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/.coveragerc0000644000076500000240000000074114716225054014645 0ustar00twstaff[run] branch = True disable_warnings = module-not-measured source = src/borg omit = */borg/__init__.py */borg/__main__.py */borg/_version.py */borg/fuse.py */borg/support/* */borg/testsuite/* */borg/hash_sizes.py [report] exclude_lines = pragma: no cover pragma: freebsd only pragma: unknown platform only def __repr__ raise AssertionError raise NotImplementedError if 0: if __name__ == .__main__.: ignore_errors = True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/.git-blame-ignore-revs0000644000076500000240000000010714716225054016620 0ustar00twstaff# Migrate code style to Black 7957af562d5ce8266b177039783be4dc8bdd7898 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/.pre-commit-config.yaml0000644000076500000240000000027014716225054017002 0ustar00twstaffrepos: - repo: https://github.com/psf/black rev: 24.8.0 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.0.287 hooks: - id: ruff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/.readthedocs.yaml0000644000076500000240000000125714716225054015756 0ustar00twstaff# .readthedocs.yaml - Read the Docs configuration file. # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details. version: 2 build: os: ubuntu-22.04 tools: python: "3.11" jobs: post_checkout: - git fetch --unshallow apt_packages: - build-essential - pkg-config - libacl1-dev - libssl-dev - liblz4-dev - libzstd-dev - libxxhash-dev python: install: - requirements: requirements.d/development.lock.txt - requirements: requirements.d/docs.txt - method: pip path: . sphinx: configuration: docs/conf.py formats: - htmlzip ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/AUTHORS0000644000076500000240000000223314716225054013572 0ustar00twstaffE-mail addresses listed here are not intended for support, please see the `support section`_ instead. .. _support section: https://borgbackup.readthedocs.io/en/stable/support.html Borg authors ("The Borg Collective") ------------------------------------ - Thomas Waldmann - Radek Podgorny - Yuri D'Elia - Michael Hanselmann - Teemu Toivanen - Marian Beermann - Martin Hostettler - Daniel Reichelt - Lauri Niskanen - Abdel-Rahman A. (Abogical) - Gu1nness - Andrey Andreyevich Bienkowski Retired ``````` - Antoine Beaupré Borg is a fork of Attic. Attic authors ------------- Attic is written and maintained by Jonas Borgström and various contributors: Attic Development Lead `````````````````````` - Jonas Borgström Attic Patches and Suggestions ````````````````````````````` - Brian Johnson - Cyril Roussillon - Dan Christensen - Jeremy Maitin-Shepard - Johann Klähn - Petros Moisiadis - Thomas Waldmann ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/Brewfile0000644000076500000240000000043514716225054014206 0ustar00twstaffbrew 'pkg-config' brew 'zstd' brew 'lz4' brew 'xxhash' brew 'openssl@3.0' # osxfuse (aka macFUSE) is only required for "borg mount", # but won't work on github actions' workers. # it requires installing a kernel extension, so some users # may want it and some won't. #cask 'osxfuse' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/CHANGES.rst0000644000076500000240000012216014716225054014326 0ustar00twstaff.. _important_notes: Important notes 2.x =================== This section provides information about security and corruption issues. (nothing to see here yet) .. _upgradenotes2: Upgrade Notes ============= borg 1.2.x/1.4.x to borg 2.0 ---------------------------- Compatibility notes: - this is a major "breaking" release that is not compatible with existing repos. We tried to put all the necessary "breaking" changes into this release, so we hopefully do not need another breaking release in the near future. The changes were necessary for improved security, improved speed and parallelism, unblocking future improvements, getting rid of legacy crap and design limitations, having less and simpler code to maintain. You can use "borg transfer" to transfer archives from borg 1.2/1.4 repos to a new borg 2.0 repo, but it will need some time and space. Before using "borg transfer", you must have upgraded to borg >= 1.2.6 (or another borg version that was patched to fix CVE-2023-CVE-2023-36811) and you must have followed the upgrade instructions at top of the change log relating to manifest and archive TAMs (borg2 just requires these TAMs now). - command line syntax was changed, scripts and wrappers will need changes: - you will usually either export BORG_REPO= into your environment or call borg like: "borg -r ". in the docs, we usually omit "-r ..." for brevity. - the scp-style REPO syntax was removed, please use ssh://..., #6697 - ssh:// URLs: removed support for /~otheruser/, /~/ and /./, #6855. New format: - ssh://user@host:port/relative/path - ssh://user@host:port//absolute/path - -P / --prefix option was removed, please use the similar -a / --match-archives. - archive names don't need to be unique anymore. to the contrary: it is now strongly recommended to use the identical name for borg create within the same series of archives to make borg work more efficiently. the name now identifies a series of archive, to identify a single archive please use aid:, e.g.: borg delete aid:d34db33f - in case you do NOT want to adopt the "series name" way of naming archives (like "myarchive") as we recommend, but keep using always-changing names (like "myserver-myarchive-20241231"), you can do that, but then you must make use of BORG_FILES_CACHE_SUFFIX and either set it to a constant suffix (like "all") or to a unique suffix per archive series (like "myserver-myarchive") so that borg can find the correct files cache. For the "all" variant, you must also set BORG_FILES_CACHE_TTL to a value greater than the count of different archives series you write to that repo. Usually borg uses a different files cache suffix per archive (series) name and defaults to BORG_FILES_CACHE_TTL=2 because that is sufficient for that. - the archive id is always given separately from the repository (differently than with borg 1.x you must not give repo::archive). - the series name or archive id is either given as a positional parameter, like: - borg create documents ~/Documents - borg diff aid:deadbeef aid:d34db33f - or, if the command makes sense for an arbitrary amount of archives, archives can be selected using a glob pattern, like: - borg delete -a 'sh:myarchive-2024-??-??' - borg recreate -a 'sh:myarchive-2024-??-??' - some borg 1.x commands that supported working on a repo AND on an archive were split into 2 commands, some others were renamed: - borg 2 repo commands: - borg repo-create # was: borg init - borg repo-list - borg repo-info - borg repo-delete - borg repo-compress - borg repo-space - borg 2 archive commands: - borg create NAME ... - borg list ID - borg extract ID ... - borg diff ID1 ID2 - borg rename ID NEWNAME - borg info ID - borg delete ID - borg recreate ID ... - borg mount -a ID mountpoint ... For more details, please consult the docs or --help option output. - create/recreate/import-tar --timestamp: defaults to local timezone now (was: UTC) - some deprecated options were removed: - removed --remote-ratelimit (use --upload-ratelimit) - removed --numeric-owner (use --numeric-ids) - removed --nobsdflags (use --noflags) - removed --noatime (default now, see also --atime) - removed --save-space option (does not change behaviour) - removed --bypass-lock option - removed borg config command (only worked locally anyway) - compact command now requires access to the borg key if the repo is encrypted or authenticated - using --list together with --progress is now disallowed (except with --log-json), #7219 - the --glob-archives option was renamed to --match-archives (the short option name -a is unchanged) and extended to support different pattern styles: - id: for identical string match (this is the new default!) - sh: for shell pattern / globbing match (this was used by --glob-archives) - re: for regular expression match So you might need to edit your scripts like e.g.:: borg 1.x: --glob-archives 'myserver-2024-*' borg 2.0: --match-archives 'sh:myserver-2024-*' - use platformdirs 3.x.x instead of home-grown code. Due to that: - XDG_*_HOME is not honoured on macOS and on Windows. - BORG_BASE_DIR can still be used to enforce some base dir + .config/ or .cache/. - the default macOS config and cache dir will now be in ~/Library/Application Support/borg/. - create: different included/excluded status chars, #7321 - dry-run: now uses "+" (was: "-") and "-" (was: "x") for included/excluded status - non-dry-run: now uses "-" (was: "x") for excluded files Option --filter=... might need an update, if you filter for the status chars that were changed. - borg is now more strict and disallows giving some options multiple times - if that makes no sense. Highlander options, see #6269. That might make scripts fail now that somehow "worked" before (but maybe didn't work as intended due to the contradicting options). .. _changelog: Change Log 2.x ============== Version 2.0.0b14 (2024-11-17) ----------------------------- Please note: Beta releases are only for testing on NEW repos - do not use for production. For upgrade and compatibility hints, please also read the section "Upgrade Notes" above. New features: - delete: now only soft-deletes archives (same for prune) - repo-list: --deleted lists deleted archives - undelete: undelete soft-deleted archives, #8500 Fixes: - chunks index cache: - enable partial/incremental updates (F_NEW flag). - write chunks index every 10mins, #8503. this makes sure progress is not totally lost when a backup is interrupted. - write to repo/cache/chunks. to enable parallel updates. - mount: fix check_pending_archive to give correct root dir, #8528 Other changes: - repo-compress: reduce memory consumption (F_COMPRESS flag) - files cache: reduce memory consumption, #5756 - check: rename --undelete-archives to --find-lost-archives - check: rebuild_archives_directory: accelerate by only reading metadata - shell completions: adapt zsh for borg 2.0.0b13 - needs more work! - chunk index: rename .refcount to .flags, use it for user and system flags. - vagrant: - add bookworm32 box for 32bit platform testing - fix pythons on freebsd14 - simplify openindiana box setup - docs: - remove --bypass-lock, small changes regarding compression - FAQ: clean up entries regarding SSH settings Version 2.0.0b13 (2024-10-31) ----------------------------- New features: - implement special tags, @PROT tag for protecting archives, #953. borg won't delete/prune/recreate protected archives. - prune: add quarterly pruning strategy, #8337. - import-tar/export-tar: add xattr support for PAX format, #2521. Fixes: - simple error msgs for existing / non-existing repo, no tracebacks, #8475. - mount: create unique directory names, #8461. - diff: suppress modified changes for files which weren't actually modified. - diff: do not test for ctime difference on windows. - prune: fix exception when NAME is given, #8486 - repo-create: build and cache an empty ChunkIndex. - work around missing size/nfiles archive metadata, #8491 - lock after checking repo exists, #8485 Other changes: - new file:, rclone:, ssh:, sftp: URLs, #8372, #8446. new way to deal with absolute vs. relative paths. - require borgstore ~= 0.1.0, require borghash ~= 0.0.1. - new hashtable code based on borghash project: - borghash replaces old / hard to maintain _hashindex.c code. - implement ChunkIndex, NSIndex1, FuseVersionsIndex using borghash.HashTableNT. - rewrite NSIndex1 (borg 1.x) on-disk format read/write methods in Cython. - remove NSIndex (early borg2) data structure / serialization code for repo index. - change xxh64 seed for ChunkIndex to invalidate old cache contents. - chunks index: show hashtable stats at debug log level, #506. - check (repository part): build and cache a ChunkIndex. check (archives part): use cached ChunkIndex from check (repository part). - export-tar: switch default to PAX format. - docs: - update URL docs - mount: document on-demand loading, perf tips, #7173. - borg/borgfs detects internally under which name it was invoked, #8207. - better link modern return codes, #8370. - binary: using the directory build is faster, #8008. - update "Running the tests (using the pypi package)", #6386. - github CI: - temporarily disabled windows CI, #8474. - msys2: use pyinstaller 6.10.0. - msys2: install rclone. - tests: - rename test files so that pytest default discovery finds them. - call register_assert_rewrite before importing borg.testsuite. - move conftest.py one directory level higher. - remove hashindex tests from selftests (borghash project has own tests). Version 2.0.0b12 (2024-10-03) ----------------------------- New features: - tag: new command to set, add, remove tags. - repo-list: add tags/hostname/username/comment to default format, reorder, adjust. Idea: not putting these into the archive name, but keeping them separate. - repo-list --short: only print archive IDs (unique IDs, used for scripting). - implement --match-archives user:USERNAME host:HOSTNAME tags:TAG1,TAG2,... - allow -a / --match-archives multiple times (logical AND). E.g.: borg delete -a home -a user:kenny -a host:kenny-pc - analyze: list changed chunks' sizes per directory. Fixes: - locking: also refresh the lock in other repo methods. avoid repo lock getting stale when processing lots of unchanged files, #8442. - make sure the store gets closed in case of exceptions, #8413. - msgpack: increase max_buffer_size to ~4GiB, #8440. - Location.canonical_path: fix protocol and host display, #8446. Other changes: - give borgstore.Store a complete levels configuration, #8432. - add BORG_STORE_DATA_LEVELS=2 env var. - check: also display archive timestamp. - vagrant: - use python 3.12.6 for binary builds. - new testing box based on bento/ubuntu-24.04. - install Rust on BSD. Version 2.0.0b11 (2024-09-26) ----------------------------- New features: - Support rclone:// URLs for borg repositories. This enables 70+ cloud storage products, including Amazon S3, Backblaze B2, Ceph, Dropbox, ftp(s), Google Cloud Storage, Google Drive, Microsoft Azure, Microsoft OneDrive, OpenStack Swift, pCloud, Seafile, sftp, SMB / CIFS and WebDAV! See https://rclone.org/ for more details. - Parallel operations in same repo from same client (same user/machine). - Archive series feature, #7930. TL;DR: a NAME now identifies a series of identically named archives, to identify a specific single archive, use aid:. in borg 1.x, we used to put a timestamp into the archive name, because borg1 required unique archive names. borg2 does not require unique archive names, but it encourages you to even use a identical archive names within the same SERIES of archives, e.g. you could backup user files to archives named "user-files" and system files to archives named "system-files". that makes matching (e.g. for prune, for the files cache, ...) much simpler and borg now KNOWS which archives belong to the same series (because they all have the same name). - info/delete/prune: allow positional NAME argument, e.g.: - borg prune --keep-daily 30 - borg delete aid: - create: also archive inode number, #8362 Borg can use this when using archive series to rebuild the local files cache from the previous archive (of the same series) in the repository. Fixes: - Remove superfluous repository.list() call. for high latency repos (like sftp, cloud), this improves performance of borg check and compact. - repository.list: refresh lock more frequently - misc. commands fixed for non-unique archive names - remote: allow get_manifest method - files cache: fix rare race condition with data loss potential, #3536 - storelocking: misc. fixes / cleanups Other changes: - Cache the chunks index in the repository, #8397. Improves high latency repo performance for most commands compared to b10. - repo-compress: faster by using chunks index rather than repository.list(). - Files cache entries now have both ctime AND mtime. - Borg updates the ctime and mtime of known and "unchanged" files, #4915. - Rebuild files cache from previous archive in same series, #8385. - Reduce RAM usage by splitting the files cache by archive series, #5658. - Remove AdHocCache, remove BORG_CACHE_IMPL (we only have one implementation). - Docs: user@ and :port are optional in sftp and ssh URLs. - CI: re-enable windows build after fixing it. - Upgrade pyinstaller to 6.10.0. - Increase IDS_PER_CHUNK, #6945. Version 2.0.0b10 (2024-09-09) ----------------------------- New features: - borgstore based repository, file:, ssh: and sftp: for now, more possible. - repository stores objects separately now, not using segment files. this has more fs overhead, but needs much less I/O because no segment files compaction is required anymore. also, no repository index is needed anymore because we can directly find the objects by their ID. - locking: new borgstore based repository locking with automatic stale lock removal (if lock does not get refreshed, if lock owner process is dead). - simultaneous repository access for many borg commands except check/compact. the cache lock for adhocwithfiles is still exclusive though, so use BORG_CACHE_IMPL=adhoc if you want to try that out using only 1 machine and 1 user (that implementation doesn't use a cache lock). When using multiple client machines or users, it also works with the default cache. - delete/prune: much quicker now and can be undone. - check --repair --undelete-archives: bring archives back from the dead. - repo-space: manage reserved space in repository (avoid dead-end situation if repository filesystem runs full). Bugs/issues fixed: - a lot! all linked from PR #8332. Other changes: - repository: remove transactions, solved differently and much simpler now (convergence and write order primarily). - repository: replaced precise reference counting with "object exists in repo?" and "garbage collection of unused objects". - cache: remove transactions, remove chunks cache. removed LocalCache, BORG_CACHE_IMPL=local, solving all related issues. as in beta 9, adhowwithfiles is the default implementation. - compact: needs the borg key now (run it clientside), -v gives nice stats. - transfer: archive transfers from borg 1.x need the --from-borg1 option - check: reimplemented / bigger changes. - code: got rid of a metric ton of not needed complexity. when borg does not need to read borg 1.x repos/archives anymore, after users have transferred their archives, even much more can be removed. - docs: updated / removed outdated stuff - renamed r* commands to repo-* Version 2.0.0b9 (2024-07-20) ---------------------------- New features: - add BORG_CACHE_IMPL, default is "adhocwithfiles" to test the new cache implementation, featuring an adhoc non-persistent chunks cache and a persistent files cache. See the docs for other values. Requires to run "borg check --repair --archives-only" to delete orphaned chunks before running "borg compact" to free space! These orphans are expected due to the simplified refcounting with the AdHocFilesCache. - make BORG_EXIT_CODES="modern" the default, #8110 - add BORG_USE_CHUNKS_ARCHIVE env var, #8280 - automatically rebuild cache on exception, #5213 Bug fixes: - fix Ctrl-C / SIGINT behaviour for pyinstaller-made binaries, #8155 - delete: fix error handling with Ctrl-C - rcompress: fix error handling with Ctrl-C - delete: fix error handling when no archive is specified, #8256 - setup.py: fix import error reporting for cythonize import, see #8208 - create: deal with EBUSY, #8123 - benchmark: inherit options --rsh --remote-path, #8099 - benchmark: fix return value, #8113 - key export: fix crash when no path is given, fix exception handling Other changes: - setup.py: detect noexec build fs issue, see #8208 - improve acl_get / acl_set error handling (forward port from 1.4-maint) - allow msgpack 1.1.0 - vagrant: use pyinstaller 6.7.0 - use Python 3.11.9 for binary builds - require Cython 3.0.3 at least, #8133 - docs: add non-root deployment strategy Version 2.0.0b8 (2024-02-20) ---------------------------- New features: - create: add the slashdot hack, update docs, #4685 - BORG_EXIT_CODES=modern: optional more specific return codes (for errors and warnings). The default value of this new environment variable is "legacy", which should result in a behaviour similar to borg 1.2 and older (only using rc 0, 1 and 2). "modern" exit codes are much more specific (see the internals/frontends docs). - implement "borg version" (shows client and server version), #7829 Fixes: - docs: CVE-2023-36811 upgrade steps: consider checkpoint archives, #7802 - check/compact: fix spurious reappearance of orphan chunks since borg 1.2, #6687 - this consists of 2 fixes: - for existing chunks: check --repair: recreate shadow index, #7897 #6687 - for newly created chunks: update shadow index when doing a double-put, #7896 #5661 If you have experienced issue #6687, you may want to run borg check --repair after upgrading to borg 1.2.7 to recreate the shadow index and get rid of the issue for existing chunks. - check: fix return code for index entry value discrepancies - LockRoster.modify: no KeyError if element was already gone, #7937 - create --X-from-command: run subcommands with a clean environment, #7916 - list --sort-by: support "archive" as alias of "name", #7873 - fix rc and msg if arg parsing throws an exception, #7885 - PATH: do not accept empty strings, #4221 - fix invalid pattern argument error msg - zlib legacy decompress fixes, #7883 Other changes: - replace archive/manifest TAMs by typed repo objects (ro_type), docs, #7670 - crypto: use a one-step kdf for session keys, #7953 - remove recreate --recompress option, use the more efficient repo-wide "rcompress". - include unistd.h in _chunker.c (fix for Python 3.13) - allow msgpack 1.0.7 - allow platformdirs 4, #7950 - use and require cython3 - move conftest.py to src/borg/testsuite, #6386 - use less setup.py, use pip and build - linux: use pkgconfig to find libacl - borg.logger: use same method params as python logging - create and use Brewfile, document "brew bundle" install (macOS) - blacken master branch - prevent CLI argument issues in scripts/glibc_check.py - pyproject.toml: exclude source files which have been compiled, #7828 - sdist: dynamically compute readme (long_description) - init: better borg key export instructions - scripts/make.py: move clean, build_man, build_usage to there, so we do not need to invoke setup.py directly, update docs - vagrant: - use openssl 3.0 on macOS - add script for fetching borg binaries from VMs, #7989 - use generic/openbsd7 box - netbsd: test on py311 only - remove debian 9 "stretch" box - use freebsd 14, #6871 - use python 3.9.4 for tests, latest python 3.11.7 for binary builds - use pyinstaller 6.3.0 - docs: - add typical PR workflow to development docs, #7495 - improve docs for borg with-lock, add example #8024 - create disk/partition sector backup by disk serial number - Add "check.rebuild_refcounts" message - not only attack/unsafe, can also be a fs issue, #7853 - use virtualenv on Cygwin - readthedocs: also build offline docs, #7835 - do not refer to setup.py installation method - how to run the testsuite using the dist package - requirements are defined in pyproject.toml Version 2.0.0b7 (2023-09-14) ---------------------------- New features: - BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without having the borg key, #7700 Fixes: - archive tam verify security fix, fixes CVE-2023-36811 - remote logging/progress: use callback to send queued records, #7662 - make_path_safe: remove test for backslashes, #7651 - benchmark cpu: use sanitized path, #7654 - create: do not try to read parent dir of recursion root, #7746 Other changes: - always implicitly require archive TAMs (all archives have TAMs since borg 1.2.6) - always implicitly require manifest TAMs (manifests have TAMs since borg 1.0.9) - rlist: remove support for {tam} placeholder, archives are now always TAM-authenticated. - support / test on Python 3.12 - allow msgpack 1.0.6 (which has py312 wheels), #7810 - manifest: move item_keys into config dict (manifest.version == 2 now), #7710 - replace "datetime.utcfromtimestamp" to avoid deprecation warnings with Python 3.12 - properly normalise paths on Windows (forward slashes, integrate drive letter into path) - Docs: - move upgrade / compat. notes to own section, see #7546 - fix borg delete examples, #7759 - improve rcreate / related repos docs - automated-local.rst: use UUID for consistent udev rule - rewrite `borg check` docs, #7578 - misc. other docs updates - Tests / CI / Vagrant: - major testsuite refactoring: a lot more tests now use pytest, #7626 - freebsd: add some ACL tests, #7745 - fix test_disk_full, #7617 - fix failing test_get_runtime_dir test on OpenBSD, #7719 - CI: run on ubuntu 22.04 - CI: test building the docs - simplify flake8 config, fix some complaints - use pyinstaller 5.13.1 to build the borg binaries Version 2.0.0b6 (2023-06-11) ---------------------------- New features: - diff: include changes in ctime and mtime, #7248 - diff: sort JSON output alphabetically - diff --content-only: option added to ignore metadata changes - diff: add --format option, #4634 - import-tar --ignore-zeros: new option to support importing concatenated tars, #7432 - debug id-hash / parse-obj / format-obj: new debug commands, #7406 - transfer --compression=C --recompress=M: recompress while transferring, #7529 - extract --continue: continue a previously interrupted extraction, #1356 - prune --list-kept/--list-pruned: only list the kept (or pruned) archives, #7511 - prune --short/--format: enable users to format the list output, #3238 - implement BORG__FORMAT env vars for prune, list, rlist, #5166 - rlist: size and nfiles format keys - implement unix domain (ipc) socket support, #6183:: borg serve --socket # server side (not started automatically!) borg -r socket:///path/to/repo ... # client side - add get_runtime_dir / BORG_RUNTIME_DIR (contains e.g. .sock and .pid file) - support shell-style alternatives, like: sh:image.{png,jpg}, #7602 Fixes: - do not retry on permission errors (pointless) - transfer: verify chunks we get using assert_id, #7383 - fix config/cache dir compatibility issues, #7445 - xattrs: fix namespace processing on FreeBSD, #6997 - ProgressIndicatorPercent: fix space computation for wide chars, #3027 - delete: remove --cache-only option, #7440. for deleting the cache only, use: borg rdelete --cache-only - borg debug get-obj/put-obj: fixed chunk id - create: ignore empty paths, print warning, #5637 - extract: support extraction of atime/mtime on win32 - benchmark crud: use TemporaryDirectory below given path, #4706 - Ensure that cli options specified with action=Highlander can only be set once, even if the set value is a default value. Add tests for action=Highlander, #7500, #6269. - Fix argparse error messages from misc. validators (being more specific). - put security infos into data dir, add BORG_DATA_DIR env var, #5760 - setup.cfg: remove setup_requires (we have a pyproject.toml for that), #7574 - do not crash for empty archives list in borg rlist date based matching, #7522 - sanitize paths during archive creation and extraction, #7108 #7099 - make sure we do not get backslashes into item paths Other changes: - allow msgpack 1.0.5 also - development.lock.txt: upgrade cython to 0.29.35, misc. other upgrades - clarify platformdirs requirements, #7393. 3.0.0 is only required for macOS due to breaking changes. 2.6.0 was the last breaking change for Linux/UNIX. - mount: improve mountpoint error msgs, see #7496 - more Highlander options, #6269 - Windows: simplify building (just use pip) - refactor toplevel exception handling, #6018 - remove nonce management, related repo methods (not needed for borg2) - borg.remote: remove support for borg < 1.1.0 ($LOG, logging setup, exceptions, rpc tuple data format, version) - new remote and progress logging, #7604 - borg.logger: add logging debugging functionality - add function to clear empty directories at end of compact process - unify scanning and listing of segment dirs / segment files, #7597 - replace `LRUCache` internals with `OrderedDict` - docs: - add installation instructions for Windows - improve --one-file-system help and docs (macOS APFS), #5618 #4876 - BORG_KEY_FILE: clarify docs, #7444 - installation: add link to OS dependencies, #7356 - update FAQ about locale/unicode issues, #6999 - improve mount options rendering, #7359 - make timestamps in manual pages reproducible. - describe performing pull-backups via ssh remote forwarding - suggest to use forced command when using remote-forwarding via ssh - fix some -a / --match-archives docs issues - incl./excl. options header, clarify --path-from-stdin exclusive control - add note about MAX_DATA_SIZE - update security support docs - improve patterns help - CI / tests / vagrant: - added pre-commit for linting purposes, #7476 - resolved mode bug and added sleep clause for darwin systems, #7470 - "auto" compressor tests: do not assume zlib is better than lz4, #7363 - add stretch64 VM with deps built from source - misc. other CI / test fixes and updates - vagrant: add lunar64 VM, fix packages_netbsd - avoid long ids in pytest output - tox: package = editable-legacy, #7580 - tox under fakeroot: fix finding setup_docs, #7391 - check buzhash chunksize distribution, #7586 - use debian/bookworm64 box Version 2.0.0b5 (2023-02-27) ---------------------------- New features: - create: implement retries for individual fs files (e.g. if a file changed while we read it, if a file had an OSError) - info: add used storage quota, #7121 - transfer: support --progress - create/recreate/import-tar: add --checkpoint-volume option - support date-based matching for archive selection, add --newer/--older/--newest/--oldest options, #7062 #7296 Fixes: - disallow --list with --progress, #7219 - create: fix --list --dry-run output for directories, #7209 - do no assume hardlink_master=True if not present, #7175 - fix item_ptrs orphaned chunks of checkpoint archives - avoid orphan content chunks on BackupOSError, #6709 - transfer: fix bug in obfuscated data upgrade code - fs.py: fix bug in f-string (thanks mypy!) - recreate: when --target is given, do not detect "nothing to do", #7254 - locking (win32): deal with os.rmdir/listdir PermissionErrors - locking: thread id must be parsed as hex from lock file name - extract: fix mtime when ResourceFork xattr is set (macOS specific), #7234 - recreate: without --chunker-params borg shall not rechunk, #7336 - allow mixing --progress and --list in log-json mode - add "files changed while reading" to Statistics class, #7354 - fixed keys determination in Statistics.__add__(), #7355 Other changes: - use local time / local timezone to output timestamps, #7283 - update development.lock.txt, including a setuptools security fix, #7227 - remove --save-space option (does not change behaviour) - remove part files from final archive - remove --consider-part-files, related stats code, update docs - transfer: drop part files - check: show id of orphaned chunks - ArchiveItem.cmdline list-of-str -> .command_line str, #7246 - Item: symlinks: rename .source to .target, #7245 - Item: make user/group/uid/gid optional - create: do not store user/group for stdin data by default, #7249 - extract: chown only if we have u/g info in archived item, #7249 - export-tar: for items w/o uid/gid, default to 0/0, #7249 - fix some uid/gid lookup code / tests for win32 - cache.py: be less verbose during cache sync - update bash completion script commands and options, #7273 - require and use platformdirs 3.x.x package, tests - better included/excluded status chars, docs, #7321 - undef NDEBUG for chunker and hashindex (make assert() work) - assert_id: better be paranoid (add back same crypto code as in old borg), #7362 - check --verify_data: always decompress and call assert_id(), #7362 - make hashindex_compact simpler and probably faster, minor fixes, cleanups, more tests - hashindex minor fixes, refactor, tweaks, tests - pyinstaller: remove icon - validation / placeholders / JSON: - implement (text|binary)_to_json: key (text), key_b64 (base64(binary)) - remove bpath, barchive, bcomment placeholders / JSON keys - archive metadata: make sure hostname and username have no surrogate escapes - text attributes (like archive name, comment): validate more strictly, #2290 - transfer: validate archive names and comment before transfer - json output: use text_to_json (path, target), #6151 - docs: - docs and comments consistency, readability and spelling fixes - fix --progress display description, #7180 - document how borg deals with non-unicode bytes in JSON output - document another way to get UTF-8 encoding on stdin/stdout/stderr, #2273 - pruning interprets timestamps in the local timezone where borg prune runs - shellpattern: add license, use copyright/license markup - key change-passphrase: fix --encryption value in examples - remove BORG_LIBB2_PREFIX (not used any more) - Installation: Update Fedora in distribution list, #7357 - add .readthedocs.yaml (use py311, use non-shallow clone) - tests: - fix archiver tests on Windows, add running the tests to Windows CI - fix tox4 passenv issue, #7199 - github actions updates (fix deprecation warnings) - add tests for borg transfer/upgrade - fix test hanging reading FIFO when `borg create` failed - mypy inspired fixes / updates - fix prune tests, prune in localtime - do not look up uid 0 / gid 0, but current process uid/gid - safe_unlink tests: use os.link to support win32 also - fix test_size_on_disk_accurate for large st_blksize, #7250 - relaxed timestamp comparisons, use same_ts_ns - add test for extracted directory mtime - use "fail" chunker to test erroneous input file skipping Version 2.0.0b4 (2022-11-27) ---------------------------- Fixes: - transfer/upgrade: fix borg < 1.2 chunker_params, #7079 - transfer/upgrade: do not access Item._dict, #7077 - transfer/upgrade: fix crash in borg transfer, #7156 - archive.save(): always use metadata from stats, #7072 - benchmark: fixed TypeError in compression benchmarks, #7075 - fix repository.scan api minimum requirement - fix args.paths related argparsing, #6994 Other changes: - tar_filter: recognize .tar.zst as zstd, #7093 - adding performance statistics to borg create, #6991 - docs: add rcompress to usage index - tests: - use github and MSYS2 for Windows CI, #7097 - win32 and cygwin: test fixes / skip hanging test - vagrant / github CI: use python 3.11.0 / 3.10.8 - vagrant: - upgrade pyinstaller to 5.6.2 (supports python 3.11) - use python 3.11 to build the borg binary Version 2.0.0b3 (2022-10-02) ---------------------------- Fixes: - transfer: fix user/group == None crash with borg1 archives - compressors: avoid memoryview related TypeError - check: fix uninitialised variable if repo is completely empty, #7034 - do not use version_tuple placeholder in setuptools_scm template, #7024 - get_chunker: fix missing sparse=False argument, #7056 New features: - rcompress: do a repo-wide (re)compression, #7037 - implement pattern support for --match-archives, #6504 - BORG_LOCK_WAIT=n env var to set default for --lock-wait option, #5279 Other: - repository.scan: misc. fixes / improvements - metadata: differentiate between empty/zero and unknown, #6908 - CI: test pyfuse3 with python 3.11 - use more relative imports - make borg.testsuite.archiver a package, split archiver tests into many modules - support reading new, improved hashindex header format, #6960. added version number and num_empty to the HashHeader, fixed alignment. - vagrant: upgrade pyinstaller 4.10 -> 5.4.1, use python 3.9.14 for binary build - item.pyx: use more Cython (faster, uses less memory), #5763 Version 2.0.0b2 (2022-09-10) ---------------------------- Bug fixes: - xattrs / extended stat: improve exception handling, #6988 - fix and refactor replace_placeholders, #6966 New features: - support archive timestamps with utc offsets, adapt them when using borg transfer to transfer from borg 1.x repos (append +00:00 for UTC). - create/recreate/import-tar --timestamp: accept giving timezone via its utc offset. defaults to local timezone, if no utc offset is given. Other changes: - chunks: have separate encrypted metadata (ctype, clevel, csize, size) chunk = enc_meta_len16 + encrypted(msgpacked(meta)) + encrypted(compressed(data)). this breaks repo format compatibility, you need to create fresh repos! - repository api: flags support, #6982 - OpenBSD only - statically link OpenSSL, #6474. Avoid conflicting with shared libcrypto from the base OS pulled in via dependencies. - restructured source code - update diagrams to odg format, #6928 Version 2.0.0b1 (2022-08-08) ---------------------------- New features: - massively increase archive metadata stream size limit, #1473. currently rather testing the code, scalability will improve later, see #6945. - rcreate --copy-crypt-key: copy crypt_key from key of other repo, #6710. default: create new, random authenticated encryption key. - prune/delete --checkpoint-interval=1800 and ctrl-c/SIGINT support, #6284 Fixes: - ctrl-c must not kill important subprocesses, #6912 - transfer: check whether ID hash method and chunker secret are same. add PlaintextKey and AuthenticatedKey support to uses_same_id_hash function. - check: try harder to create the key, #5719 - SaveFile: use a custom mkstemp with mode support, #6933, #6400 - make setuptools happy, #6874 - fix misc. compiler warnings - list: fix {flags:} formatting, #6081 Other changes: - new crypto does not need to call ._assert_id(), update code and docs. https://github.com/borgbackup/borg/pull/6463#discussion_r925436156 - check: --verify-data does not need to decompress with new crypto modes - Key: crypt_key instead of enc_key + enc_hmac_key, #6611 - misc. docs updates and improvements - CI: test on macOS 12 without fuse / fuse tests - repository: add debug logging for issue #6687 - _version.py: remove trailing blank, add LF at EOF (make pep8 checker happy) Version 2.0.0a4 (2022-07-17) ---------------------------- New features: - recreate: consider level for recompression, #6698, #3622 Other changes: - stop using libdeflate - CI: add mypy (if we add type hints, it can do type checking) - big changes to the source code: - split up archiver module, transform it into a package - use Black for automated code formatting - remove some legacy code - adapt/fix code for mypy - use language_level = 3str for cython (this will be the default in cython 3) - docs: document HardLinkManager and hlid, #2388 Version 2.0.0a3 (2022-07-04) ---------------------------- Fixes: - check repo version, accept old repos only for --other-repo (e.g. rcreate/transfer). v2 is the default repo version for borg 2.0. v1 repos must only be used in a read-only way, e.g. for --other-repo=V1_REPO with borg init and borg transfer! New features: - transfer: --upgrader=NoOp is the default. This is to support general-purpose transfer of archives between related borg2 repos. - transfer: --upgrader=From12To20 must be used to transfer (and convert) archives from borg 1.2 repos to borg 2.0 repos. Other changes: - removed some deprecated options - removed -P (aka --prefix) option, #6806. The option -a (aka --glob-archives) can be used for same purpose and is more powerful, e.g.: -a 'PREFIX*' - rcreate: always use argon2 kdf for new repos, #6820 - rcreate: remove legacy encryption modes for new repos, #6490 Version 2.0.0a2 (2022-06-26) ---------------------------- Changes: - split repo and archive name into separate args, #948 - use -r or --repo or BORG_REPO env var to give the repository - use --other-repo or BORG_OTHER_REPO to give another repo (e.g. borg transfer) - use positional argument for archive name or `-a ARCH_GLOB` - remove support for scp-style repo specification, use ssh://... - simplify stats output: repo ops -> repo stats, archive ops -> archive stats - repository index: add payload size (==csize) and flags to NSIndex entries - repository index: set/query flags, iteration over flagged items (NSIndex) - repository: sync write file in get_fd - stats: deduplicated size now, was deduplicated compressed size in borg 1.x - remove csize support at most places in the code (chunks index, stats, get_size, Item.chunks) - replace problematic/ugly hardlink_master approach of borg 1.x by: - symmetric hlid (all hardlinks pointing to same inode have same hlid) - all archived hardlinked regular files have a chunks list - borg rcreate --other-repo=OTHER_REPO: reuse key material from OTHER_REPO, #6554. This is useful if you want to use borg transfer to transfer archives from an existing borg 1.1/1.2 repo. If the chunker secret and the id key and algorithm stay the same, the deduplication will also work between past and future backups. - borg transfer: - efficiently copy archives from a borg 1.1/1.2 repo to a new repo. uses deduplication and does not decompress/recompress file content data. - does some cleanups / fixes / conversions: - disallow None value for .user/group/chunks/chunks_healthy - cleanup msgpack related str/bytes mess, use new msgpack spec, #968 - obfuscation: fix byte order for size, #6701 - compression: use the 2 bytes for type and level, #6698 - use version 2 for new archives - convert timestamps int/bigint -> msgpack.Timestamp, see #2323 - all hardlinks have chunks, maybe chunks_healthy, hlid - remove the zlib type bytes hack - make sure items with chunks have precomputed size - removes the csize element from the tuples in the Item.chunks list - clean item of attic 0.13 'acl' bug remnants - crypto: see 1.3.0a1 log entry - removed "borg upgrade" command (not needed any more) - compact: removed --cleanup-commits option - docs: fixed quickstart and usage docs with new cli command syntax - docs: removed the parts talking about potential AES-CTR mode issues (we will not use that any more). Version 1.3.0a1 (2022-04-15) ---------------------------- Although this was released as 1.3.0a1, it can be also seen as 2.0.0a1 as it was later decided to do breaking changes and thus the major release number had to be increased (thus, there will not be a 1.3.0 release, but 2.0.0). New features: - init: new --encryption=(repokey|keyfile)-[blake2-](aes-ocb|chacha20-poly1305) - New, better, faster crypto (see encryption-aead diagram in the docs), #6463. - New AEAD cipher suites: AES-OCB and CHACHA20-POLY1305. - Session keys are derived via HKDF from random session id and master key. - Nonces/MessageIVs are counters starting from 0 for each session. - AAD: chunk id, key type, messageIV, sessionID are now authenticated also. - Solves the potential AES-CTR mode counter management issues of the legacy crypto. - init: --key-algorithm=argon2 (new default KDF, older pbkdf2 also still available) borg key change-passphrase / change-location keeps the key algorithm unchanged. - key change-algorithm: to upgrade existing keys to argon2 or downgrade to pbkdf2. We recommend you to upgrade unless you have to keep the key compatible with older versions of borg. - key change-location: usable for repokey <-> keyfile location change - benchmark cpu: display benchmarks of cpu bound stuff - export-tar: new --tar-format=PAX (default: GNU) - import-tar/export-tar: can use PAX format for ctime and atime support - import-tar/export-tar: --tar-format=BORG: roundtrip ALL item metadata, #5830 - repository: create and use version 2 repos only for now - repository: implement PUT2: header crc32, overall xxh64, #1704 Other changes: - require python >= 3.9, #6315 - simplify libs setup, #6482 - unbundle most bundled 3rd party code, use libs, #6316 - use libdeflate.crc32 (Linux and all others) or zlib.crc32 (macOS) - repository: code cleanups / simplifications - internal crypto api: speedups / cleanups / refactorings / modernisation - remove "borg upgrade" support for "attic backup" repos - remove PassphraseKey code and borg key migrate-to-repokey command - OpenBSD: build borg with OpenSSL (not: LibreSSL), #6474 - remove support for LibreSSL, #6474 - remove support for OpenSSL < 1.1.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/LICENSE0000644000076500000240000000301714716225054013530 0ustar00twstaffCopyright (C) 2015-2024 The Borg Collective (see AUTHORS file) Copyright (C) 2010-2014 Jonas Borgström All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/MANIFEST.in0000644000076500000240000000073614716225054014266 0ustar00twstaff# stuff we need to include into the sdist is handled automatically by # setuptools_scm - it includes all git-committed files. # but we want to exclude some committed files/dirs not needed in the sdist: exclude .editorconfig .gitattributes .gitignore .mailmap Vagrantfile prune .github include src/borg/platform/darwin.c src/borg/platform/freebsd.c src/borg/platform/linux.c src/borg/platform/posix.c include src/borg/platform/syncfilerange.c include src/borg/platform/windows.c ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.7404687 borgbackup-2.0.0b14/PKG-INFO0000644000076500000240000002002614716232130013610 0ustar00twstaffMetadata-Version: 2.1 Name: borgbackup Version: 2.0.0b14 Summary: Deduplicated, encrypted, authenticated and compressed backups Author: The Borg Collective (see AUTHORS file) Maintainer-email: Thomas Waldmann License: BSD Project-URL: Homepage, https://borgbackup.org/ Project-URL: Bug Tracker, https://github.com/borgbackup/borg/issues Project-URL: Documentation, https://borgbackup.readthedocs.io/ Project-URL: Repository, https://github.com/borgbackup/borg Project-URL: Changelog, https://github.com/borgbackup/borg/blob/master/docs/changes.rst Keywords: backup,borgbackup Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: BSD :: OpenBSD Classifier: Operating System :: POSIX :: BSD :: NetBSD Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Topic :: Security :: Cryptography Classifier: Topic :: System :: Archiving :: Backup Requires-Python: >=3.9 License-File: LICENSE License-File: AUTHORS Requires-Dist: borghash~=0.0.1 Requires-Dist: borgstore~=0.1.0 Requires-Dist: msgpack<=1.1.0,>=1.0.3 Requires-Dist: packaging Requires-Dist: platformdirs<5.0.0,>=3.0.0; sys_platform == "darwin" Requires-Dist: platformdirs<5.0.0,>=2.6.0; sys_platform != "darwin" Requires-Dist: argon2-cffi Provides-Extra: llfuse Requires-Dist: llfuse>=1.3.8; extra == "llfuse" Provides-Extra: pyfuse3 Requires-Dist: pyfuse3>=3.1.1; extra == "pyfuse3" Provides-Extra: nofuse What is BorgBackup? ------------------- BorgBackup (short: Borg) is a deduplicating backup program. Optionally, it supports compression and authenticated encryption. The main goal of Borg is to provide an efficient and secure way to back up data. The data deduplication technique used makes Borg suitable for daily backups since only changes are stored. The authenticated encryption technique makes it suitable for backups to targets not fully trusted. See the `installation manual`_ or, if you have already downloaded Borg, ``docs/installation.rst`` to get started with Borg. There is also an `offline documentation`_ available, in multiple formats. .. _installation manual: https://borgbackup.readthedocs.org/en/stable/installation.html .. _offline documentation: https://readthedocs.org/projects/borgbackup/downloads Main features ~~~~~~~~~~~~~ **Space efficient storage** Deduplication based on content-defined chunking is used to reduce the number of bytes stored: each file is split into a number of variable length chunks and only chunks that have never been seen before are added to the repository. A chunk is considered duplicate if its id_hash value is identical. A cryptographically strong hash or MAC function is used as id_hash, e.g. (hmac-)sha256. To deduplicate, all the chunks in the same repository are considered, no matter whether they come from different machines, from previous backups, from the same backup or even from the same single file. Compared to other deduplication approaches, this method does NOT depend on: * file/directory names staying the same: So you can move your stuff around without killing the deduplication, even between machines sharing a repo. * complete files or time stamps staying the same: If a big file changes a little, only a few new chunks need to be stored - this is great for VMs or raw disks. * The absolute position of a data chunk inside a file: Stuff may get shifted and will still be found by the deduplication algorithm. **Speed** * performance-critical code (chunking, compression, encryption) is implemented in C/Cython * local caching * quick detection of unmodified files **Data encryption** All data can be protected client-side using 256-bit authenticated encryption (AES-OCB or chacha20-poly1305), ensuring data confidentiality, integrity and authenticity. **Obfuscation** Optionally, borg can actively obfuscate e.g. the size of files / chunks to make fingerprinting attacks more difficult. **Compression** All data can be optionally compressed: * lz4 (super fast, low compression) * zstd (wide range from high speed and low compression to high compression and lower speed) * zlib (medium speed and compression) * lzma (low speed, high compression) **Off-site backups** Borg can store data on any remote host accessible over SSH. If Borg is installed on the remote host, big performance gains can be achieved compared to using a network filesystem (sshfs, nfs, ...). **Backups mountable as filesystems** Backup archives are mountable as userspace filesystems for easy interactive backup examination and restores (e.g. by using a regular file manager). **Easy installation on multiple platforms** We offer single-file binaries that do not require installing anything - you can just run them on these platforms: * Linux * macOS * FreeBSD * OpenBSD and NetBSD (no xattrs/ACLs support or binaries yet) * Cygwin (experimental, no binaries yet) * Linux Subsystem of Windows 10 (experimental) **Free and Open Source Software** * security and functionality can be audited independently * licensed under the BSD (3-clause) license, see `License`_ for the complete license Easy to use ~~~~~~~~~~~ For ease of use, set the BORG_REPO environment variable:: $ export BORG_REPO=/path/to/repo Create a new backup repository (see ``borg repo-create --help`` for encryption options):: $ borg repo-create -e repokey-aes-ocb Create a new backup archive:: $ borg create Monday1 ~/Documents Now doing another backup, just to show off the great deduplication:: $ borg create -v --stats Monday2 ~/Documents Repository: /path/to/repo Archive name: Monday2 Archive fingerprint: 7714aef97c1a24539cc3dc73f79b060f14af04e2541da33d54c7ee8e81a00089 Time (start): Mon, 2022-10-03 19:57:35 +0200 Time (end): Mon, 2022-10-03 19:57:35 +0200 Duration: 0.01 seconds Number of files: 24 Original size: 29.73 MB Deduplicated size: 520 B Helping, Donations and Bounties, becoming a Patron -------------------------------------------------- Your help is always welcome! Spread the word, give feedback, help with documentation, testing or development. You can also give monetary support to the project, see there for details: https://www.borgbackup.org/support/fund.html Links ----- * `Main Web Site `_ * `Releases `_, `PyPI packages `_ and `ChangeLog `_ * `Offline Documentation `_ * `GitHub `_ and `Issue Tracker `_. * `Web-Chat (IRC) `_ and `Mailing List `_ * `License `_ * `Security contact `_ Compatibility notes ------------------- EXPECT THAT WE WILL BREAK COMPATIBILITY REPEATEDLY WHEN MAJOR RELEASE NUMBER CHANGES (like when going from 0.x.y to 1.0.0 or from 1.x.y to 2.0.0). NOT RELEASED DEVELOPMENT VERSIONS HAVE UNKNOWN COMPATIBILITY PROPERTIES. THIS IS SOFTWARE IN DEVELOPMENT, DECIDE YOURSELF WHETHER IT FITS YOUR NEEDS. Security issues should be reported to the `Security contact`_ (or see ``docs/support.rst`` in the source distribution). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/README.rst0000644000076500000240000001750714716225054014223 0ustar00twstaffThis is borg2! -------------- Please note that this is the README for borg2 / master branch. For the stable version's docs, please see there: https://borgbackup.readthedocs.io/en/stable/ Borg2 is currently in beta testing and might get major and/or breaking changes between beta releases (and there is no beta to next-beta upgrade code, so you will have to delete and re-create repos). Thus, **DO NOT USE BORG2 FOR YOUR PRODUCTION BACKUPS!** Please help with testing it, but set it up *additionally* to your production backups. TODO: the screencasts need a remake using borg2, see there: https://github.com/borgbackup/borg/issues/6303 What is BorgBackup? ------------------- BorgBackup (short: Borg) is a deduplicating backup program. Optionally, it supports compression and authenticated encryption. The main goal of Borg is to provide an efficient and secure way to back up data. The data deduplication technique used makes Borg suitable for daily backups since only changes are stored. The authenticated encryption technique makes it suitable for backups to targets not fully trusted. See the `installation manual`_ or, if you have already downloaded Borg, ``docs/installation.rst`` to get started with Borg. There is also an `offline documentation`_ available, in multiple formats. .. _installation manual: https://borgbackup.readthedocs.org/en/stable/installation.html .. _offline documentation: https://readthedocs.org/projects/borgbackup/downloads Main features ~~~~~~~~~~~~~ **Space efficient storage** Deduplication based on content-defined chunking is used to reduce the number of bytes stored: each file is split into a number of variable length chunks and only chunks that have never been seen before are added to the repository. A chunk is considered duplicate if its id_hash value is identical. A cryptographically strong hash or MAC function is used as id_hash, e.g. (hmac-)sha256. To deduplicate, all the chunks in the same repository are considered, no matter whether they come from different machines, from previous backups, from the same backup or even from the same single file. Compared to other deduplication approaches, this method does NOT depend on: * file/directory names staying the same: So you can move your stuff around without killing the deduplication, even between machines sharing a repo. * complete files or time stamps staying the same: If a big file changes a little, only a few new chunks need to be stored - this is great for VMs or raw disks. * The absolute position of a data chunk inside a file: Stuff may get shifted and will still be found by the deduplication algorithm. **Speed** * performance-critical code (chunking, compression, encryption) is implemented in C/Cython * local caching * quick detection of unmodified files **Data encryption** All data can be protected client-side using 256-bit authenticated encryption (AES-OCB or chacha20-poly1305), ensuring data confidentiality, integrity and authenticity. **Obfuscation** Optionally, borg can actively obfuscate e.g. the size of files / chunks to make fingerprinting attacks more difficult. **Compression** All data can be optionally compressed: * lz4 (super fast, low compression) * zstd (wide range from high speed and low compression to high compression and lower speed) * zlib (medium speed and compression) * lzma (low speed, high compression) **Off-site backups** Borg can store data on any remote host accessible over SSH. If Borg is installed on the remote host, big performance gains can be achieved compared to using a network filesystem (sshfs, nfs, ...). **Backups mountable as filesystems** Backup archives are mountable as userspace filesystems for easy interactive backup examination and restores (e.g. by using a regular file manager). **Easy installation on multiple platforms** We offer single-file binaries that do not require installing anything - you can just run them on these platforms: * Linux * macOS * FreeBSD * OpenBSD and NetBSD (no xattrs/ACLs support or binaries yet) * Cygwin (experimental, no binaries yet) * Linux Subsystem of Windows 10 (experimental) **Free and Open Source Software** * security and functionality can be audited independently * licensed under the BSD (3-clause) license, see `License`_ for the complete license Easy to use ~~~~~~~~~~~ For ease of use, set the BORG_REPO environment variable:: $ export BORG_REPO=/path/to/repo Create a new backup repository (see ``borg repo-create --help`` for encryption options):: $ borg repo-create -e repokey-aes-ocb Create a new backup archive:: $ borg create Monday1 ~/Documents Now doing another backup, just to show off the great deduplication:: $ borg create -v --stats Monday2 ~/Documents Repository: /path/to/repo Archive name: Monday2 Archive fingerprint: 7714aef97c1a24539cc3dc73f79b060f14af04e2541da33d54c7ee8e81a00089 Time (start): Mon, 2022-10-03 19:57:35 +0200 Time (end): Mon, 2022-10-03 19:57:35 +0200 Duration: 0.01 seconds Number of files: 24 Original size: 29.73 MB Deduplicated size: 520 B Helping, Donations and Bounties, becoming a Patron -------------------------------------------------- Your help is always welcome! Spread the word, give feedback, help with documentation, testing or development. You can also give monetary support to the project, see there for details: https://www.borgbackup.org/support/fund.html Links ----- * `Main Web Site `_ * `Releases `_, `PyPI packages `_ and `ChangeLog `_ * `Offline Documentation `_ * `GitHub `_ and `Issue Tracker `_. * `Web-Chat (IRC) `_ and `Mailing List `_ * `License `_ * `Security contact `_ Compatibility notes ------------------- EXPECT THAT WE WILL BREAK COMPATIBILITY REPEATEDLY WHEN MAJOR RELEASE NUMBER CHANGES (like when going from 0.x.y to 1.0.0 or from 1.x.y to 2.0.0). NOT RELEASED DEVELOPMENT VERSIONS HAVE UNKNOWN COMPATIBILITY PROPERTIES. THIS IS SOFTWARE IN DEVELOPMENT, DECIDE YOURSELF WHETHER IT FITS YOUR NEEDS. Security issues should be reported to the `Security contact`_ (or see ``docs/support.rst`` in the source distribution). .. start-badges |doc| |build| |coverage| |bestpractices| .. |doc| image:: https://readthedocs.org/projects/borgbackup/badge/?version=stable :alt: Documentation :target: https://borgbackup.readthedocs.org/en/stable/ .. |build| image:: https://github.com/borgbackup/borg/workflows/CI/badge.svg?branch=master :alt: Build Status (master) :target: https://github.com/borgbackup/borg/actions .. |coverage| image:: https://codecov.io/github/borgbackup/borg/coverage.svg?branch=master :alt: Test Coverage :target: https://codecov.io/github/borgbackup/borg?branch=master .. |screencast_basic| image:: https://asciinema.org/a/133292.png :alt: BorgBackup Basic Usage :target: https://asciinema.org/a/133292?autoplay=1&speed=1 :width: 100% .. _installation: https://asciinema.org/a/133291?autoplay=1&speed=1 .. _advanced usage: https://asciinema.org/a/133293?autoplay=1&speed=1 .. |bestpractices| image:: https://bestpractices.coreinfrastructure.org/projects/271/badge :alt: Best Practices Score :target: https://bestpractices.coreinfrastructure.org/projects/271 .. end-badges ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/SECURITY.md0000644000076500000240000000065114716225054014315 0ustar00twstaff# Security Policy ## Supported Versions These borg releases are currently supported with security updates. | Version | Supported | |---------|--------------------| | 2.0.x | :x: (not released) | | 1.2.x | :white_check_mark: | | 1.1.x | :x: | | < 1.1 | :x: | ## Reporting a Vulnerability See there: https://borgbackup.readthedocs.io/en/latest/support.html#security-contact ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.5895505 borgbackup-2.0.0b14/docs/0000755000076500000240000000000014716232130013443 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.5898373 borgbackup-2.0.0b14/docs/3rd_party/0000755000076500000240000000000014716232130015352 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/3rd_party/README0000644000076500000240000000032714716225054016243 0ustar00twstaffHere we store 3rd party documentation, licenses, etc. Please note that all files inside the "borg" package directory (except the stuff excluded in setup.py) will be INSTALLED, so don't keep docs or licenses there. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/Makefile0000644000076500000240000001074614716225054015122 0ustar00twstaff# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/borg.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/borg.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/borg" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/borg" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.5913973 borgbackup-2.0.0b14/docs/_static/0000755000076500000240000000000014716232130015071 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_static/Makefile0000644000076500000240000000030614716225054016537 0ustar00twstaff all: logo.pdf logo.png logo.pdf: logo.svg inkscape logo.svg --export-pdf=logo.pdf logo.png: logo.svg inkscape logo.svg --export-png=logo.png --export-dpi=72,72 clean: rm -f logo.pdf logo.png ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_static/favicon.ico0000644000076500000240000011401614716225054017224 0ustar00twstaffRR m6RRFmRR"&u(R PP aaaa PPKL<M>>>>>>?BuL0i9iuL0i9iuL0i9iCuL%|cs] cvu"uL4uL4uL4uL<>>>4B\\\\\3uL0 4uL064uL0-4uL`* 3e`Q=Lf9CflFLg9 glHLh9 hlJL%|L0L0L0PP aaaa PP?@@@@@@?(R(R "%*-03469<=>?BCFHJKLMQ\]`cefghilsuv|S\\\\\\\\' R\\\\\\\\\\(\\\\\\\\\\\\) !Q\\\\\"G\\\\\6(\\\\\U\\\\\\\;3\\\\N\\\Q9\\\\\D3\\\UG\\\\\6(\\\\\U\\\\\\\\;3\\\\\N\\\\P 9\\\\\D3\\\\\UG\\\\\6(\\\\\U\\\\\\\\\;3\\\\\\N\\\\\P 9\\\\\D3\\\\\\\#G\\\\\6(\\\\\UEGGGW\\\\\8.\\\\\T5,>\\\\\N 9\\\\\D.\\\\\L76G\\\\\6(\\\\\U<\\\\\PD\\\\\@\\\\\\9\\\\\DD\\\\\<G\\\\\6(\\\\\U<\\\\\PD\\\\\@\\\\\\9\\\\\DD\\\\\<G\\\\\6(\\\\\U<\\\\\PD\\\\\@\\\\\\9\\\\\DD\\\\\<G\\\\\6(\\\\\U  A\\\\\?D\\\\\@\\\\\\9\\\\\D"+++++D\\\\\<G\\\\\6(\\\\\U\\\\\\\\\AD\\\\\@\\\\\\9\\\\\DF\\\\\<D\\\\\<G\\\\\6(\\\\\U\\\\\\\\\D\\\\\@\\\\\\9\\\\\DF\\\\\<D\\\\\<G\\\\\6(\\\\\U\\\\\\\\\XD\\\\\@\\\\\\9\\\\\DF\\\\\<D\\\\\<G\\\\\6(\\\\\U<\\\\\M-\\\\\YH=R\\\\\L 9\\\\\DIJZ\\\\\/-\\\\\<*JJ[\\\\\(\\\\\U<\\\\\P0\\\\\\N\\\\\N 9\\\\\D#\\\\\\\@0\\\\<4\\\\\\\$(\\\\\U<\\\\\P1\\\\\N\\\\O 9\\\\\DB\\\\\K 1\\\<4\\\\\\%(\\\\\U<\\\\\P2\\\\N\\\Q 9\\\\\DC\\\V2\\<4\\\\\&(\\\\\UEGGGW\\\\\8(\\\\\U\\\\\\\\\:(\\\\\U\\\\\\\\:(\\\\\U\\\\\\\:././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_static/logo.pdf0000644000076500000240000000244414716225054016537 0ustar00twstaff%PDF-1.4 % 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream x}TAr! _@PyB!{H5ʞi  '?ƏvI0&t=ގZ=N*Q,J|DP ? ÜA-\ $k@ֽdхY3ko6T_,30^'w4=BJ5~AƑ`3WhF%$ acܒļRx|k]MX)`xLpRZ:>4V'}8Y( G||⥾ hs?$ J}( VR%Že3鯵"4 5)`OĹ){-֗Y<˹uPm؎mmV)jVtn y"9C$.}_T~ԫwXx/QXj^}5Rx'b.n5}S endstream endobj 4 0 obj 430 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> >> endobj 5 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 240 100 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 2 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 5 0 R ] /Count 1 >> endobj 6 0 obj << /Creator (cairo 1.14.8 (http://cairographics.org)) /Producer (cairo 1.14.8 (http://cairographics.org)) >> endobj 7 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 8 0000000000 65535 f 0000000830 00000 n 0000000544 00000 n 0000000015 00000 n 0000000522 00000 n 0000000616 00000 n 0000000895 00000 n 0000001022 00000 n trailer << /Size 8 /Root 7 0 R /Info 6 0 R >> startxref 1074 %%EOF ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_static/logo.png0000644000076500000240000000337514716225054016556 0ustar00twstaffPNG  IHDRdsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<zIDATx]e񯛽iFeaFPPHIeIKA^EQAAwEEb/FQkJkn=E9Ywg8z؇S Hq X0,b1LE S")`a X0,b1LE S")`a X0,b1LE S"Zz.u^ǀcD/Áik3x+B V&"QF"~ E,Nq G|z+"/AKc01(b1/݀Ay~'`ee)* oq.%o7XwoT16 8zOb`v%pJ-(ιL/~x"~_x0xvb7Sr1=m1LO,a 1ś)19La pdoc~(B&F<x81Ǜ)!n/8_{bE#Gr.KE2tHwԽ͓.Hp61FE S")`a X0,b1,ObsgӎzE\ xν#86'ULo?O\7Jrܹ}2Ώ$ xN pfjl.\c n};IvNw"Ni獂,t7|UwTnÝ4=!i; zZhL@ -R,R,M`/Vbq&p_k,)^Pt;pSy7|4_?nKMEmƽUTr%-y͸%3LKZ&elnC]ha X0,b1LE S"S gGIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_static/logo.svg0000644000076500000240000000337614716225054016572 0ustar00twstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_static/logo_font.txt0000644000076500000240000000015714716225054017632 0ustar00twstaffBlack Ops One James Grieshaber SIL Open Font License, 1.1 https://www.google.com/fonts/specimen/Black+Ops+One ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.5917914 borgbackup-2.0.0b14/docs/_templates/0000755000076500000240000000000014716232130015600 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_templates/globaltoc.html0000644000076500000240000000117114716225054020443 0ustar00twstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/_templates/logo-text.html0000644000076500000240000000024014716225054020413 0ustar00twstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/authors.rst0000644000076500000240000000021314716225054015665 0ustar00twstaff.. include:: global.rst.inc Authors ======= .. include:: ../AUTHORS License ======= .. _license: .. include:: ../LICENSE :literal: ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1731802199.592096 borgbackup-2.0.0b14/docs/binaries/0000755000076500000240000000000014716232130015237 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/binaries/00_README.txt0000644000076500000240000000544714716225054017255 0ustar00twstaffBinary BorgBackup builds ======================== The binaries are supposed to work on the specified platform without installing any dependencies. Download the correct files -------------------------- amd64 / x86_64 architecture ~~~~~~~~~~~~~~~~~~~~~~~~~~~ borg-linux-glibc236 Linux (built on Debian 12 "Bookworm" with glibc 2.36) borg-linux-glibc231 Linux (built on Debian 11 "Bullseye" with glibc 2.31) Note: you can also try them on other Linuxes with other glibc versions - as long as the glibc is compatible, they will work. If it doesn't work, try a borg 1.4.x or 1.2.x binary. borg-macos1012 macOS (built on macOS Sierra 10.12 with latest macFUSE from brew) To avoid signing issues download the file via command line OR remove the "quarantine" attribute after downloading: $ xattr -dr com.apple.quarantine borg-macos.tgz borg-freebsd14 FreeBSD (built on FreeBSD 14) *.tgz similar to above, but built as a directory with files, not as a single self-extracting binary. using the directory build is faster and doesn't need as much space in the temp directory as the one-file build. *.asc GnuPG signatures for * Verifying your download ----------------------- Please check the GPG signature to make sure you received the binary as I have built it. To check the GPG signature, download both the binary and the corresponding *.asc file and then (on the shell) type, e.g.: gpg --recv-keys 9F88FB52FAF7B393 gpg --verify borg-freebsd14.asc borg-freebsd14 The files are signed by: Thomas Waldmann GPG key fingerprint: 6D5B EF9A DD20 7580 5747 B70F 9F88 FB52 FAF7 B393 My fingerprint is also in the footer of all my borgbackup mailing list posts. Installing ---------- It is suggested that you rename or symlink the binary to just "borg". If you need "borgfs", just also symlink it to the same binary, it will detect internally under which name it was invoked. On UNIX-like platforms, /usr/local/bin/ or ~/bin/ is a nice place for it, but you can invoke it from every place by giving a full path to it. Make sure the file is readable and executable (chmod +rx borg on UNIX-like platforms). Reporting issues ---------------- If you find issues, please open a ticket on our issue tracker: https://github.com/borgbackup/borg/issues/ There, please give: - the version number (it is displayed if you invoke borg -V) - the sha256sum of the binary - a good description of what the issue is - a good description of how to reproduce your issue - a traceback with system info (if you have one) - your precise platform (CPU, 32/64bit?), OS, distribution, release - your python and (g)libc version ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/book.rst0000644000076500000240000000062114716225054015135 0ustar00twstaff:orphan: .. include:: global.rst.inc Borg documentation ================== .. when you add an element here, do not forget to add it to index.rst .. Note: Some things are in appendices (see latex_appendices in conf.py) .. toctree:: :maxdepth: 2 introduction installation quickstart usage deployment faq support changes internals development authors ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.5756764 borgbackup-2.0.0b14/docs/borg_theme/0000755000076500000240000000000014716232130015556 5ustar00twstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.5922835 borgbackup-2.0.0b14/docs/borg_theme/css/0000755000076500000240000000000014716232130016346 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/borg_theme/css/borg.css0000644000076500000240000000762114716225054020026 0ustar00twstaff@import url("theme.css"); dt code { font-weight: normal; } #internals .toctree-wrapper > ul { column-count: 3; -webkit-column-count: 3; } #internals .toctree-wrapper > ul > li { display: inline-block; font-weight: bold; } #internals .toctree-wrapper > ul > li > ul { font-weight: normal; } /* bootstrap has a .container class which clashes with docutils' container class. */ .docutils.container { width: auto; margin: 0; padding: 0; } /* the default (38px) produces a jumpy baseline in Firefox on Linux. */ h1 { font-size: 36px; } .text-logo { background-color: #000200; color: #00dd00; } .text-logo:hover, .text-logo:active, .text-logo:focus { color: #5afe57; } /* by default the top and bottom margins are unequal which looks a bit unbalanced. */ .sidebar-block { padding: 0; margin: 14px 0 24px 0; } #borg-documentation h1 + p .external img { width: 100%; } .container.experimental, #debugging-facilities { /* don't change text dimensions */ margin: 0 -30px; /* padding below + border width */ padding: 0 10px; /* 10 px visual margin between edge of text and the border */ /* fallback for browsers that don't have repeating-linear-gradient: thick, red lines */ border-left: 20px solid red; border-right: 20px solid red; /* fancy red stripes */ border-image: repeating-linear-gradient( -45deg,rgba(255,0,0,0.1) 0,rgba(255,0,0,0.75) 10px,rgba(0,0,0,0) 10px,rgba(0,0,0,0) 20px,rgba(255,0,0,0.75) 20px) 0 20 repeat; } .topic { margin: 0 1em; padding: 0 1em; /* #4e4a4a = background of the ToC sidebar */ border-left: 2px solid #4e4a4a;; border-right: 2px solid #4e4a4a;; } table.docutils:not(.footnote) td, table.docutils:not(.footnote) th { padding: .2em; } table.docutils:not(.footnote) { border-collapse: collapse; border: none; } table.docutils:not(.footnote) td, table.docutils:not(.footnote) th { border: 1px solid #ddd; } table.docutils:not(.footnote) tr:first-child th, table.docutils:not(.footnote) tr:first-child td { border-top: 0; } table.docutils:not(.footnote) tr:last-child td { border-bottom: 0; } table.docutils:not(.footnote) tr td:first-child, table.docutils:not(.footnote) tr th:first-child { border-left: 0; } table.docutils:not(.footnote) tr td:last-child, table.docutils:not(.footnote) tr th:last-child, table.docutils.borg-options-table tr td { border-right: 0; } table.docutils.option-list tr td, table.docutils.borg-options-table tr td { border-left: 0; border-right: 0; } table.docutils.borg-options-table tr td:first-child:not([colspan="3"]) { border-top: 0; border-bottom: 0; } .borg-options-table td[colspan="3"] p { margin: 0; } .borg-options-table { width: 100%; } kbd, /* used in usage pages for options */ code, .rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal, .rst-content tt, .rst-content code, p .literal, p .literal span { border: none; padding: 0; color: black; /* slight contrast with #404040 of regular text */ background: none; } kbd { box-shadow: none; line-height: 23px; word-wrap: normal; font-size: 15px; font-family: Consolas, monospace; } .borg-options-table tr td:nth-child(2) .pre { white-space: nowrap; } .borg-options-table tr td:first-child { width: 2em; } cite { white-space: nowrap; color: black; /* slight contrast with #404040 of regular text */ font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-style: normal; text-decoration: underline; } .borg-common-opt-ref { font-weight: bold; } .sidebar-toc ul li.toctree-l2 a, .sidebar-toc ul li.toctree-l3 a { padding-right: 25px; } #common-options .option { white-space: nowrap; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/changes.rst0000644000076500000240000012216014716225054015616 0ustar00twstaff.. _important_notes: Important notes 2.x =================== This section provides information about security and corruption issues. (nothing to see here yet) .. _upgradenotes2: Upgrade Notes ============= borg 1.2.x/1.4.x to borg 2.0 ---------------------------- Compatibility notes: - this is a major "breaking" release that is not compatible with existing repos. We tried to put all the necessary "breaking" changes into this release, so we hopefully do not need another breaking release in the near future. The changes were necessary for improved security, improved speed and parallelism, unblocking future improvements, getting rid of legacy crap and design limitations, having less and simpler code to maintain. You can use "borg transfer" to transfer archives from borg 1.2/1.4 repos to a new borg 2.0 repo, but it will need some time and space. Before using "borg transfer", you must have upgraded to borg >= 1.2.6 (or another borg version that was patched to fix CVE-2023-CVE-2023-36811) and you must have followed the upgrade instructions at top of the change log relating to manifest and archive TAMs (borg2 just requires these TAMs now). - command line syntax was changed, scripts and wrappers will need changes: - you will usually either export BORG_REPO= into your environment or call borg like: "borg -r ". in the docs, we usually omit "-r ..." for brevity. - the scp-style REPO syntax was removed, please use ssh://..., #6697 - ssh:// URLs: removed support for /~otheruser/, /~/ and /./, #6855. New format: - ssh://user@host:port/relative/path - ssh://user@host:port//absolute/path - -P / --prefix option was removed, please use the similar -a / --match-archives. - archive names don't need to be unique anymore. to the contrary: it is now strongly recommended to use the identical name for borg create within the same series of archives to make borg work more efficiently. the name now identifies a series of archive, to identify a single archive please use aid:, e.g.: borg delete aid:d34db33f - in case you do NOT want to adopt the "series name" way of naming archives (like "myarchive") as we recommend, but keep using always-changing names (like "myserver-myarchive-20241231"), you can do that, but then you must make use of BORG_FILES_CACHE_SUFFIX and either set it to a constant suffix (like "all") or to a unique suffix per archive series (like "myserver-myarchive") so that borg can find the correct files cache. For the "all" variant, you must also set BORG_FILES_CACHE_TTL to a value greater than the count of different archives series you write to that repo. Usually borg uses a different files cache suffix per archive (series) name and defaults to BORG_FILES_CACHE_TTL=2 because that is sufficient for that. - the archive id is always given separately from the repository (differently than with borg 1.x you must not give repo::archive). - the series name or archive id is either given as a positional parameter, like: - borg create documents ~/Documents - borg diff aid:deadbeef aid:d34db33f - or, if the command makes sense for an arbitrary amount of archives, archives can be selected using a glob pattern, like: - borg delete -a 'sh:myarchive-2024-??-??' - borg recreate -a 'sh:myarchive-2024-??-??' - some borg 1.x commands that supported working on a repo AND on an archive were split into 2 commands, some others were renamed: - borg 2 repo commands: - borg repo-create # was: borg init - borg repo-list - borg repo-info - borg repo-delete - borg repo-compress - borg repo-space - borg 2 archive commands: - borg create NAME ... - borg list ID - borg extract ID ... - borg diff ID1 ID2 - borg rename ID NEWNAME - borg info ID - borg delete ID - borg recreate ID ... - borg mount -a ID mountpoint ... For more details, please consult the docs or --help option output. - create/recreate/import-tar --timestamp: defaults to local timezone now (was: UTC) - some deprecated options were removed: - removed --remote-ratelimit (use --upload-ratelimit) - removed --numeric-owner (use --numeric-ids) - removed --nobsdflags (use --noflags) - removed --noatime (default now, see also --atime) - removed --save-space option (does not change behaviour) - removed --bypass-lock option - removed borg config command (only worked locally anyway) - compact command now requires access to the borg key if the repo is encrypted or authenticated - using --list together with --progress is now disallowed (except with --log-json), #7219 - the --glob-archives option was renamed to --match-archives (the short option name -a is unchanged) and extended to support different pattern styles: - id: for identical string match (this is the new default!) - sh: for shell pattern / globbing match (this was used by --glob-archives) - re: for regular expression match So you might need to edit your scripts like e.g.:: borg 1.x: --glob-archives 'myserver-2024-*' borg 2.0: --match-archives 'sh:myserver-2024-*' - use platformdirs 3.x.x instead of home-grown code. Due to that: - XDG_*_HOME is not honoured on macOS and on Windows. - BORG_BASE_DIR can still be used to enforce some base dir + .config/ or .cache/. - the default macOS config and cache dir will now be in ~/Library/Application Support/borg/. - create: different included/excluded status chars, #7321 - dry-run: now uses "+" (was: "-") and "-" (was: "x") for included/excluded status - non-dry-run: now uses "-" (was: "x") for excluded files Option --filter=... might need an update, if you filter for the status chars that were changed. - borg is now more strict and disallows giving some options multiple times - if that makes no sense. Highlander options, see #6269. That might make scripts fail now that somehow "worked" before (but maybe didn't work as intended due to the contradicting options). .. _changelog: Change Log 2.x ============== Version 2.0.0b14 (2024-11-17) ----------------------------- Please note: Beta releases are only for testing on NEW repos - do not use for production. For upgrade and compatibility hints, please also read the section "Upgrade Notes" above. New features: - delete: now only soft-deletes archives (same for prune) - repo-list: --deleted lists deleted archives - undelete: undelete soft-deleted archives, #8500 Fixes: - chunks index cache: - enable partial/incremental updates (F_NEW flag). - write chunks index every 10mins, #8503. this makes sure progress is not totally lost when a backup is interrupted. - write to repo/cache/chunks. to enable parallel updates. - mount: fix check_pending_archive to give correct root dir, #8528 Other changes: - repo-compress: reduce memory consumption (F_COMPRESS flag) - files cache: reduce memory consumption, #5756 - check: rename --undelete-archives to --find-lost-archives - check: rebuild_archives_directory: accelerate by only reading metadata - shell completions: adapt zsh for borg 2.0.0b13 - needs more work! - chunk index: rename .refcount to .flags, use it for user and system flags. - vagrant: - add bookworm32 box for 32bit platform testing - fix pythons on freebsd14 - simplify openindiana box setup - docs: - remove --bypass-lock, small changes regarding compression - FAQ: clean up entries regarding SSH settings Version 2.0.0b13 (2024-10-31) ----------------------------- New features: - implement special tags, @PROT tag for protecting archives, #953. borg won't delete/prune/recreate protected archives. - prune: add quarterly pruning strategy, #8337. - import-tar/export-tar: add xattr support for PAX format, #2521. Fixes: - simple error msgs for existing / non-existing repo, no tracebacks, #8475. - mount: create unique directory names, #8461. - diff: suppress modified changes for files which weren't actually modified. - diff: do not test for ctime difference on windows. - prune: fix exception when NAME is given, #8486 - repo-create: build and cache an empty ChunkIndex. - work around missing size/nfiles archive metadata, #8491 - lock after checking repo exists, #8485 Other changes: - new file:, rclone:, ssh:, sftp: URLs, #8372, #8446. new way to deal with absolute vs. relative paths. - require borgstore ~= 0.1.0, require borghash ~= 0.0.1. - new hashtable code based on borghash project: - borghash replaces old / hard to maintain _hashindex.c code. - implement ChunkIndex, NSIndex1, FuseVersionsIndex using borghash.HashTableNT. - rewrite NSIndex1 (borg 1.x) on-disk format read/write methods in Cython. - remove NSIndex (early borg2) data structure / serialization code for repo index. - change xxh64 seed for ChunkIndex to invalidate old cache contents. - chunks index: show hashtable stats at debug log level, #506. - check (repository part): build and cache a ChunkIndex. check (archives part): use cached ChunkIndex from check (repository part). - export-tar: switch default to PAX format. - docs: - update URL docs - mount: document on-demand loading, perf tips, #7173. - borg/borgfs detects internally under which name it was invoked, #8207. - better link modern return codes, #8370. - binary: using the directory build is faster, #8008. - update "Running the tests (using the pypi package)", #6386. - github CI: - temporarily disabled windows CI, #8474. - msys2: use pyinstaller 6.10.0. - msys2: install rclone. - tests: - rename test files so that pytest default discovery finds them. - call register_assert_rewrite before importing borg.testsuite. - move conftest.py one directory level higher. - remove hashindex tests from selftests (borghash project has own tests). Version 2.0.0b12 (2024-10-03) ----------------------------- New features: - tag: new command to set, add, remove tags. - repo-list: add tags/hostname/username/comment to default format, reorder, adjust. Idea: not putting these into the archive name, but keeping them separate. - repo-list --short: only print archive IDs (unique IDs, used for scripting). - implement --match-archives user:USERNAME host:HOSTNAME tags:TAG1,TAG2,... - allow -a / --match-archives multiple times (logical AND). E.g.: borg delete -a home -a user:kenny -a host:kenny-pc - analyze: list changed chunks' sizes per directory. Fixes: - locking: also refresh the lock in other repo methods. avoid repo lock getting stale when processing lots of unchanged files, #8442. - make sure the store gets closed in case of exceptions, #8413. - msgpack: increase max_buffer_size to ~4GiB, #8440. - Location.canonical_path: fix protocol and host display, #8446. Other changes: - give borgstore.Store a complete levels configuration, #8432. - add BORG_STORE_DATA_LEVELS=2 env var. - check: also display archive timestamp. - vagrant: - use python 3.12.6 for binary builds. - new testing box based on bento/ubuntu-24.04. - install Rust on BSD. Version 2.0.0b11 (2024-09-26) ----------------------------- New features: - Support rclone:// URLs for borg repositories. This enables 70+ cloud storage products, including Amazon S3, Backblaze B2, Ceph, Dropbox, ftp(s), Google Cloud Storage, Google Drive, Microsoft Azure, Microsoft OneDrive, OpenStack Swift, pCloud, Seafile, sftp, SMB / CIFS and WebDAV! See https://rclone.org/ for more details. - Parallel operations in same repo from same client (same user/machine). - Archive series feature, #7930. TL;DR: a NAME now identifies a series of identically named archives, to identify a specific single archive, use aid:. in borg 1.x, we used to put a timestamp into the archive name, because borg1 required unique archive names. borg2 does not require unique archive names, but it encourages you to even use a identical archive names within the same SERIES of archives, e.g. you could backup user files to archives named "user-files" and system files to archives named "system-files". that makes matching (e.g. for prune, for the files cache, ...) much simpler and borg now KNOWS which archives belong to the same series (because they all have the same name). - info/delete/prune: allow positional NAME argument, e.g.: - borg prune --keep-daily 30 - borg delete aid: - create: also archive inode number, #8362 Borg can use this when using archive series to rebuild the local files cache from the previous archive (of the same series) in the repository. Fixes: - Remove superfluous repository.list() call. for high latency repos (like sftp, cloud), this improves performance of borg check and compact. - repository.list: refresh lock more frequently - misc. commands fixed for non-unique archive names - remote: allow get_manifest method - files cache: fix rare race condition with data loss potential, #3536 - storelocking: misc. fixes / cleanups Other changes: - Cache the chunks index in the repository, #8397. Improves high latency repo performance for most commands compared to b10. - repo-compress: faster by using chunks index rather than repository.list(). - Files cache entries now have both ctime AND mtime. - Borg updates the ctime and mtime of known and "unchanged" files, #4915. - Rebuild files cache from previous archive in same series, #8385. - Reduce RAM usage by splitting the files cache by archive series, #5658. - Remove AdHocCache, remove BORG_CACHE_IMPL (we only have one implementation). - Docs: user@ and :port are optional in sftp and ssh URLs. - CI: re-enable windows build after fixing it. - Upgrade pyinstaller to 6.10.0. - Increase IDS_PER_CHUNK, #6945. Version 2.0.0b10 (2024-09-09) ----------------------------- New features: - borgstore based repository, file:, ssh: and sftp: for now, more possible. - repository stores objects separately now, not using segment files. this has more fs overhead, but needs much less I/O because no segment files compaction is required anymore. also, no repository index is needed anymore because we can directly find the objects by their ID. - locking: new borgstore based repository locking with automatic stale lock removal (if lock does not get refreshed, if lock owner process is dead). - simultaneous repository access for many borg commands except check/compact. the cache lock for adhocwithfiles is still exclusive though, so use BORG_CACHE_IMPL=adhoc if you want to try that out using only 1 machine and 1 user (that implementation doesn't use a cache lock). When using multiple client machines or users, it also works with the default cache. - delete/prune: much quicker now and can be undone. - check --repair --undelete-archives: bring archives back from the dead. - repo-space: manage reserved space in repository (avoid dead-end situation if repository filesystem runs full). Bugs/issues fixed: - a lot! all linked from PR #8332. Other changes: - repository: remove transactions, solved differently and much simpler now (convergence and write order primarily). - repository: replaced precise reference counting with "object exists in repo?" and "garbage collection of unused objects". - cache: remove transactions, remove chunks cache. removed LocalCache, BORG_CACHE_IMPL=local, solving all related issues. as in beta 9, adhowwithfiles is the default implementation. - compact: needs the borg key now (run it clientside), -v gives nice stats. - transfer: archive transfers from borg 1.x need the --from-borg1 option - check: reimplemented / bigger changes. - code: got rid of a metric ton of not needed complexity. when borg does not need to read borg 1.x repos/archives anymore, after users have transferred their archives, even much more can be removed. - docs: updated / removed outdated stuff - renamed r* commands to repo-* Version 2.0.0b9 (2024-07-20) ---------------------------- New features: - add BORG_CACHE_IMPL, default is "adhocwithfiles" to test the new cache implementation, featuring an adhoc non-persistent chunks cache and a persistent files cache. See the docs for other values. Requires to run "borg check --repair --archives-only" to delete orphaned chunks before running "borg compact" to free space! These orphans are expected due to the simplified refcounting with the AdHocFilesCache. - make BORG_EXIT_CODES="modern" the default, #8110 - add BORG_USE_CHUNKS_ARCHIVE env var, #8280 - automatically rebuild cache on exception, #5213 Bug fixes: - fix Ctrl-C / SIGINT behaviour for pyinstaller-made binaries, #8155 - delete: fix error handling with Ctrl-C - rcompress: fix error handling with Ctrl-C - delete: fix error handling when no archive is specified, #8256 - setup.py: fix import error reporting for cythonize import, see #8208 - create: deal with EBUSY, #8123 - benchmark: inherit options --rsh --remote-path, #8099 - benchmark: fix return value, #8113 - key export: fix crash when no path is given, fix exception handling Other changes: - setup.py: detect noexec build fs issue, see #8208 - improve acl_get / acl_set error handling (forward port from 1.4-maint) - allow msgpack 1.1.0 - vagrant: use pyinstaller 6.7.0 - use Python 3.11.9 for binary builds - require Cython 3.0.3 at least, #8133 - docs: add non-root deployment strategy Version 2.0.0b8 (2024-02-20) ---------------------------- New features: - create: add the slashdot hack, update docs, #4685 - BORG_EXIT_CODES=modern: optional more specific return codes (for errors and warnings). The default value of this new environment variable is "legacy", which should result in a behaviour similar to borg 1.2 and older (only using rc 0, 1 and 2). "modern" exit codes are much more specific (see the internals/frontends docs). - implement "borg version" (shows client and server version), #7829 Fixes: - docs: CVE-2023-36811 upgrade steps: consider checkpoint archives, #7802 - check/compact: fix spurious reappearance of orphan chunks since borg 1.2, #6687 - this consists of 2 fixes: - for existing chunks: check --repair: recreate shadow index, #7897 #6687 - for newly created chunks: update shadow index when doing a double-put, #7896 #5661 If you have experienced issue #6687, you may want to run borg check --repair after upgrading to borg 1.2.7 to recreate the shadow index and get rid of the issue for existing chunks. - check: fix return code for index entry value discrepancies - LockRoster.modify: no KeyError if element was already gone, #7937 - create --X-from-command: run subcommands with a clean environment, #7916 - list --sort-by: support "archive" as alias of "name", #7873 - fix rc and msg if arg parsing throws an exception, #7885 - PATH: do not accept empty strings, #4221 - fix invalid pattern argument error msg - zlib legacy decompress fixes, #7883 Other changes: - replace archive/manifest TAMs by typed repo objects (ro_type), docs, #7670 - crypto: use a one-step kdf for session keys, #7953 - remove recreate --recompress option, use the more efficient repo-wide "rcompress". - include unistd.h in _chunker.c (fix for Python 3.13) - allow msgpack 1.0.7 - allow platformdirs 4, #7950 - use and require cython3 - move conftest.py to src/borg/testsuite, #6386 - use less setup.py, use pip and build - linux: use pkgconfig to find libacl - borg.logger: use same method params as python logging - create and use Brewfile, document "brew bundle" install (macOS) - blacken master branch - prevent CLI argument issues in scripts/glibc_check.py - pyproject.toml: exclude source files which have been compiled, #7828 - sdist: dynamically compute readme (long_description) - init: better borg key export instructions - scripts/make.py: move clean, build_man, build_usage to there, so we do not need to invoke setup.py directly, update docs - vagrant: - use openssl 3.0 on macOS - add script for fetching borg binaries from VMs, #7989 - use generic/openbsd7 box - netbsd: test on py311 only - remove debian 9 "stretch" box - use freebsd 14, #6871 - use python 3.9.4 for tests, latest python 3.11.7 for binary builds - use pyinstaller 6.3.0 - docs: - add typical PR workflow to development docs, #7495 - improve docs for borg with-lock, add example #8024 - create disk/partition sector backup by disk serial number - Add "check.rebuild_refcounts" message - not only attack/unsafe, can also be a fs issue, #7853 - use virtualenv on Cygwin - readthedocs: also build offline docs, #7835 - do not refer to setup.py installation method - how to run the testsuite using the dist package - requirements are defined in pyproject.toml Version 2.0.0b7 (2023-09-14) ---------------------------- New features: - BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without having the borg key, #7700 Fixes: - archive tam verify security fix, fixes CVE-2023-36811 - remote logging/progress: use callback to send queued records, #7662 - make_path_safe: remove test for backslashes, #7651 - benchmark cpu: use sanitized path, #7654 - create: do not try to read parent dir of recursion root, #7746 Other changes: - always implicitly require archive TAMs (all archives have TAMs since borg 1.2.6) - always implicitly require manifest TAMs (manifests have TAMs since borg 1.0.9) - rlist: remove support for {tam} placeholder, archives are now always TAM-authenticated. - support / test on Python 3.12 - allow msgpack 1.0.6 (which has py312 wheels), #7810 - manifest: move item_keys into config dict (manifest.version == 2 now), #7710 - replace "datetime.utcfromtimestamp" to avoid deprecation warnings with Python 3.12 - properly normalise paths on Windows (forward slashes, integrate drive letter into path) - Docs: - move upgrade / compat. notes to own section, see #7546 - fix borg delete examples, #7759 - improve rcreate / related repos docs - automated-local.rst: use UUID for consistent udev rule - rewrite `borg check` docs, #7578 - misc. other docs updates - Tests / CI / Vagrant: - major testsuite refactoring: a lot more tests now use pytest, #7626 - freebsd: add some ACL tests, #7745 - fix test_disk_full, #7617 - fix failing test_get_runtime_dir test on OpenBSD, #7719 - CI: run on ubuntu 22.04 - CI: test building the docs - simplify flake8 config, fix some complaints - use pyinstaller 5.13.1 to build the borg binaries Version 2.0.0b6 (2023-06-11) ---------------------------- New features: - diff: include changes in ctime and mtime, #7248 - diff: sort JSON output alphabetically - diff --content-only: option added to ignore metadata changes - diff: add --format option, #4634 - import-tar --ignore-zeros: new option to support importing concatenated tars, #7432 - debug id-hash / parse-obj / format-obj: new debug commands, #7406 - transfer --compression=C --recompress=M: recompress while transferring, #7529 - extract --continue: continue a previously interrupted extraction, #1356 - prune --list-kept/--list-pruned: only list the kept (or pruned) archives, #7511 - prune --short/--format: enable users to format the list output, #3238 - implement BORG__FORMAT env vars for prune, list, rlist, #5166 - rlist: size and nfiles format keys - implement unix domain (ipc) socket support, #6183:: borg serve --socket # server side (not started automatically!) borg -r socket:///path/to/repo ... # client side - add get_runtime_dir / BORG_RUNTIME_DIR (contains e.g. .sock and .pid file) - support shell-style alternatives, like: sh:image.{png,jpg}, #7602 Fixes: - do not retry on permission errors (pointless) - transfer: verify chunks we get using assert_id, #7383 - fix config/cache dir compatibility issues, #7445 - xattrs: fix namespace processing on FreeBSD, #6997 - ProgressIndicatorPercent: fix space computation for wide chars, #3027 - delete: remove --cache-only option, #7440. for deleting the cache only, use: borg rdelete --cache-only - borg debug get-obj/put-obj: fixed chunk id - create: ignore empty paths, print warning, #5637 - extract: support extraction of atime/mtime on win32 - benchmark crud: use TemporaryDirectory below given path, #4706 - Ensure that cli options specified with action=Highlander can only be set once, even if the set value is a default value. Add tests for action=Highlander, #7500, #6269. - Fix argparse error messages from misc. validators (being more specific). - put security infos into data dir, add BORG_DATA_DIR env var, #5760 - setup.cfg: remove setup_requires (we have a pyproject.toml for that), #7574 - do not crash for empty archives list in borg rlist date based matching, #7522 - sanitize paths during archive creation and extraction, #7108 #7099 - make sure we do not get backslashes into item paths Other changes: - allow msgpack 1.0.5 also - development.lock.txt: upgrade cython to 0.29.35, misc. other upgrades - clarify platformdirs requirements, #7393. 3.0.0 is only required for macOS due to breaking changes. 2.6.0 was the last breaking change for Linux/UNIX. - mount: improve mountpoint error msgs, see #7496 - more Highlander options, #6269 - Windows: simplify building (just use pip) - refactor toplevel exception handling, #6018 - remove nonce management, related repo methods (not needed for borg2) - borg.remote: remove support for borg < 1.1.0 ($LOG, logging setup, exceptions, rpc tuple data format, version) - new remote and progress logging, #7604 - borg.logger: add logging debugging functionality - add function to clear empty directories at end of compact process - unify scanning and listing of segment dirs / segment files, #7597 - replace `LRUCache` internals with `OrderedDict` - docs: - add installation instructions for Windows - improve --one-file-system help and docs (macOS APFS), #5618 #4876 - BORG_KEY_FILE: clarify docs, #7444 - installation: add link to OS dependencies, #7356 - update FAQ about locale/unicode issues, #6999 - improve mount options rendering, #7359 - make timestamps in manual pages reproducible. - describe performing pull-backups via ssh remote forwarding - suggest to use forced command when using remote-forwarding via ssh - fix some -a / --match-archives docs issues - incl./excl. options header, clarify --path-from-stdin exclusive control - add note about MAX_DATA_SIZE - update security support docs - improve patterns help - CI / tests / vagrant: - added pre-commit for linting purposes, #7476 - resolved mode bug and added sleep clause for darwin systems, #7470 - "auto" compressor tests: do not assume zlib is better than lz4, #7363 - add stretch64 VM with deps built from source - misc. other CI / test fixes and updates - vagrant: add lunar64 VM, fix packages_netbsd - avoid long ids in pytest output - tox: package = editable-legacy, #7580 - tox under fakeroot: fix finding setup_docs, #7391 - check buzhash chunksize distribution, #7586 - use debian/bookworm64 box Version 2.0.0b5 (2023-02-27) ---------------------------- New features: - create: implement retries for individual fs files (e.g. if a file changed while we read it, if a file had an OSError) - info: add used storage quota, #7121 - transfer: support --progress - create/recreate/import-tar: add --checkpoint-volume option - support date-based matching for archive selection, add --newer/--older/--newest/--oldest options, #7062 #7296 Fixes: - disallow --list with --progress, #7219 - create: fix --list --dry-run output for directories, #7209 - do no assume hardlink_master=True if not present, #7175 - fix item_ptrs orphaned chunks of checkpoint archives - avoid orphan content chunks on BackupOSError, #6709 - transfer: fix bug in obfuscated data upgrade code - fs.py: fix bug in f-string (thanks mypy!) - recreate: when --target is given, do not detect "nothing to do", #7254 - locking (win32): deal with os.rmdir/listdir PermissionErrors - locking: thread id must be parsed as hex from lock file name - extract: fix mtime when ResourceFork xattr is set (macOS specific), #7234 - recreate: without --chunker-params borg shall not rechunk, #7336 - allow mixing --progress and --list in log-json mode - add "files changed while reading" to Statistics class, #7354 - fixed keys determination in Statistics.__add__(), #7355 Other changes: - use local time / local timezone to output timestamps, #7283 - update development.lock.txt, including a setuptools security fix, #7227 - remove --save-space option (does not change behaviour) - remove part files from final archive - remove --consider-part-files, related stats code, update docs - transfer: drop part files - check: show id of orphaned chunks - ArchiveItem.cmdline list-of-str -> .command_line str, #7246 - Item: symlinks: rename .source to .target, #7245 - Item: make user/group/uid/gid optional - create: do not store user/group for stdin data by default, #7249 - extract: chown only if we have u/g info in archived item, #7249 - export-tar: for items w/o uid/gid, default to 0/0, #7249 - fix some uid/gid lookup code / tests for win32 - cache.py: be less verbose during cache sync - update bash completion script commands and options, #7273 - require and use platformdirs 3.x.x package, tests - better included/excluded status chars, docs, #7321 - undef NDEBUG for chunker and hashindex (make assert() work) - assert_id: better be paranoid (add back same crypto code as in old borg), #7362 - check --verify_data: always decompress and call assert_id(), #7362 - make hashindex_compact simpler and probably faster, minor fixes, cleanups, more tests - hashindex minor fixes, refactor, tweaks, tests - pyinstaller: remove icon - validation / placeholders / JSON: - implement (text|binary)_to_json: key (text), key_b64 (base64(binary)) - remove bpath, barchive, bcomment placeholders / JSON keys - archive metadata: make sure hostname and username have no surrogate escapes - text attributes (like archive name, comment): validate more strictly, #2290 - transfer: validate archive names and comment before transfer - json output: use text_to_json (path, target), #6151 - docs: - docs and comments consistency, readability and spelling fixes - fix --progress display description, #7180 - document how borg deals with non-unicode bytes in JSON output - document another way to get UTF-8 encoding on stdin/stdout/stderr, #2273 - pruning interprets timestamps in the local timezone where borg prune runs - shellpattern: add license, use copyright/license markup - key change-passphrase: fix --encryption value in examples - remove BORG_LIBB2_PREFIX (not used any more) - Installation: Update Fedora in distribution list, #7357 - add .readthedocs.yaml (use py311, use non-shallow clone) - tests: - fix archiver tests on Windows, add running the tests to Windows CI - fix tox4 passenv issue, #7199 - github actions updates (fix deprecation warnings) - add tests for borg transfer/upgrade - fix test hanging reading FIFO when `borg create` failed - mypy inspired fixes / updates - fix prune tests, prune in localtime - do not look up uid 0 / gid 0, but current process uid/gid - safe_unlink tests: use os.link to support win32 also - fix test_size_on_disk_accurate for large st_blksize, #7250 - relaxed timestamp comparisons, use same_ts_ns - add test for extracted directory mtime - use "fail" chunker to test erroneous input file skipping Version 2.0.0b4 (2022-11-27) ---------------------------- Fixes: - transfer/upgrade: fix borg < 1.2 chunker_params, #7079 - transfer/upgrade: do not access Item._dict, #7077 - transfer/upgrade: fix crash in borg transfer, #7156 - archive.save(): always use metadata from stats, #7072 - benchmark: fixed TypeError in compression benchmarks, #7075 - fix repository.scan api minimum requirement - fix args.paths related argparsing, #6994 Other changes: - tar_filter: recognize .tar.zst as zstd, #7093 - adding performance statistics to borg create, #6991 - docs: add rcompress to usage index - tests: - use github and MSYS2 for Windows CI, #7097 - win32 and cygwin: test fixes / skip hanging test - vagrant / github CI: use python 3.11.0 / 3.10.8 - vagrant: - upgrade pyinstaller to 5.6.2 (supports python 3.11) - use python 3.11 to build the borg binary Version 2.0.0b3 (2022-10-02) ---------------------------- Fixes: - transfer: fix user/group == None crash with borg1 archives - compressors: avoid memoryview related TypeError - check: fix uninitialised variable if repo is completely empty, #7034 - do not use version_tuple placeholder in setuptools_scm template, #7024 - get_chunker: fix missing sparse=False argument, #7056 New features: - rcompress: do a repo-wide (re)compression, #7037 - implement pattern support for --match-archives, #6504 - BORG_LOCK_WAIT=n env var to set default for --lock-wait option, #5279 Other: - repository.scan: misc. fixes / improvements - metadata: differentiate between empty/zero and unknown, #6908 - CI: test pyfuse3 with python 3.11 - use more relative imports - make borg.testsuite.archiver a package, split archiver tests into many modules - support reading new, improved hashindex header format, #6960. added version number and num_empty to the HashHeader, fixed alignment. - vagrant: upgrade pyinstaller 4.10 -> 5.4.1, use python 3.9.14 for binary build - item.pyx: use more Cython (faster, uses less memory), #5763 Version 2.0.0b2 (2022-09-10) ---------------------------- Bug fixes: - xattrs / extended stat: improve exception handling, #6988 - fix and refactor replace_placeholders, #6966 New features: - support archive timestamps with utc offsets, adapt them when using borg transfer to transfer from borg 1.x repos (append +00:00 for UTC). - create/recreate/import-tar --timestamp: accept giving timezone via its utc offset. defaults to local timezone, if no utc offset is given. Other changes: - chunks: have separate encrypted metadata (ctype, clevel, csize, size) chunk = enc_meta_len16 + encrypted(msgpacked(meta)) + encrypted(compressed(data)). this breaks repo format compatibility, you need to create fresh repos! - repository api: flags support, #6982 - OpenBSD only - statically link OpenSSL, #6474. Avoid conflicting with shared libcrypto from the base OS pulled in via dependencies. - restructured source code - update diagrams to odg format, #6928 Version 2.0.0b1 (2022-08-08) ---------------------------- New features: - massively increase archive metadata stream size limit, #1473. currently rather testing the code, scalability will improve later, see #6945. - rcreate --copy-crypt-key: copy crypt_key from key of other repo, #6710. default: create new, random authenticated encryption key. - prune/delete --checkpoint-interval=1800 and ctrl-c/SIGINT support, #6284 Fixes: - ctrl-c must not kill important subprocesses, #6912 - transfer: check whether ID hash method and chunker secret are same. add PlaintextKey and AuthenticatedKey support to uses_same_id_hash function. - check: try harder to create the key, #5719 - SaveFile: use a custom mkstemp with mode support, #6933, #6400 - make setuptools happy, #6874 - fix misc. compiler warnings - list: fix {flags:} formatting, #6081 Other changes: - new crypto does not need to call ._assert_id(), update code and docs. https://github.com/borgbackup/borg/pull/6463#discussion_r925436156 - check: --verify-data does not need to decompress with new crypto modes - Key: crypt_key instead of enc_key + enc_hmac_key, #6611 - misc. docs updates and improvements - CI: test on macOS 12 without fuse / fuse tests - repository: add debug logging for issue #6687 - _version.py: remove trailing blank, add LF at EOF (make pep8 checker happy) Version 2.0.0a4 (2022-07-17) ---------------------------- New features: - recreate: consider level for recompression, #6698, #3622 Other changes: - stop using libdeflate - CI: add mypy (if we add type hints, it can do type checking) - big changes to the source code: - split up archiver module, transform it into a package - use Black for automated code formatting - remove some legacy code - adapt/fix code for mypy - use language_level = 3str for cython (this will be the default in cython 3) - docs: document HardLinkManager and hlid, #2388 Version 2.0.0a3 (2022-07-04) ---------------------------- Fixes: - check repo version, accept old repos only for --other-repo (e.g. rcreate/transfer). v2 is the default repo version for borg 2.0. v1 repos must only be used in a read-only way, e.g. for --other-repo=V1_REPO with borg init and borg transfer! New features: - transfer: --upgrader=NoOp is the default. This is to support general-purpose transfer of archives between related borg2 repos. - transfer: --upgrader=From12To20 must be used to transfer (and convert) archives from borg 1.2 repos to borg 2.0 repos. Other changes: - removed some deprecated options - removed -P (aka --prefix) option, #6806. The option -a (aka --glob-archives) can be used for same purpose and is more powerful, e.g.: -a 'PREFIX*' - rcreate: always use argon2 kdf for new repos, #6820 - rcreate: remove legacy encryption modes for new repos, #6490 Version 2.0.0a2 (2022-06-26) ---------------------------- Changes: - split repo and archive name into separate args, #948 - use -r or --repo or BORG_REPO env var to give the repository - use --other-repo or BORG_OTHER_REPO to give another repo (e.g. borg transfer) - use positional argument for archive name or `-a ARCH_GLOB` - remove support for scp-style repo specification, use ssh://... - simplify stats output: repo ops -> repo stats, archive ops -> archive stats - repository index: add payload size (==csize) and flags to NSIndex entries - repository index: set/query flags, iteration over flagged items (NSIndex) - repository: sync write file in get_fd - stats: deduplicated size now, was deduplicated compressed size in borg 1.x - remove csize support at most places in the code (chunks index, stats, get_size, Item.chunks) - replace problematic/ugly hardlink_master approach of borg 1.x by: - symmetric hlid (all hardlinks pointing to same inode have same hlid) - all archived hardlinked regular files have a chunks list - borg rcreate --other-repo=OTHER_REPO: reuse key material from OTHER_REPO, #6554. This is useful if you want to use borg transfer to transfer archives from an existing borg 1.1/1.2 repo. If the chunker secret and the id key and algorithm stay the same, the deduplication will also work between past and future backups. - borg transfer: - efficiently copy archives from a borg 1.1/1.2 repo to a new repo. uses deduplication and does not decompress/recompress file content data. - does some cleanups / fixes / conversions: - disallow None value for .user/group/chunks/chunks_healthy - cleanup msgpack related str/bytes mess, use new msgpack spec, #968 - obfuscation: fix byte order for size, #6701 - compression: use the 2 bytes for type and level, #6698 - use version 2 for new archives - convert timestamps int/bigint -> msgpack.Timestamp, see #2323 - all hardlinks have chunks, maybe chunks_healthy, hlid - remove the zlib type bytes hack - make sure items with chunks have precomputed size - removes the csize element from the tuples in the Item.chunks list - clean item of attic 0.13 'acl' bug remnants - crypto: see 1.3.0a1 log entry - removed "borg upgrade" command (not needed any more) - compact: removed --cleanup-commits option - docs: fixed quickstart and usage docs with new cli command syntax - docs: removed the parts talking about potential AES-CTR mode issues (we will not use that any more). Version 1.3.0a1 (2022-04-15) ---------------------------- Although this was released as 1.3.0a1, it can be also seen as 2.0.0a1 as it was later decided to do breaking changes and thus the major release number had to be increased (thus, there will not be a 1.3.0 release, but 2.0.0). New features: - init: new --encryption=(repokey|keyfile)-[blake2-](aes-ocb|chacha20-poly1305) - New, better, faster crypto (see encryption-aead diagram in the docs), #6463. - New AEAD cipher suites: AES-OCB and CHACHA20-POLY1305. - Session keys are derived via HKDF from random session id and master key. - Nonces/MessageIVs are counters starting from 0 for each session. - AAD: chunk id, key type, messageIV, sessionID are now authenticated also. - Solves the potential AES-CTR mode counter management issues of the legacy crypto. - init: --key-algorithm=argon2 (new default KDF, older pbkdf2 also still available) borg key change-passphrase / change-location keeps the key algorithm unchanged. - key change-algorithm: to upgrade existing keys to argon2 or downgrade to pbkdf2. We recommend you to upgrade unless you have to keep the key compatible with older versions of borg. - key change-location: usable for repokey <-> keyfile location change - benchmark cpu: display benchmarks of cpu bound stuff - export-tar: new --tar-format=PAX (default: GNU) - import-tar/export-tar: can use PAX format for ctime and atime support - import-tar/export-tar: --tar-format=BORG: roundtrip ALL item metadata, #5830 - repository: create and use version 2 repos only for now - repository: implement PUT2: header crc32, overall xxh64, #1704 Other changes: - require python >= 3.9, #6315 - simplify libs setup, #6482 - unbundle most bundled 3rd party code, use libs, #6316 - use libdeflate.crc32 (Linux and all others) or zlib.crc32 (macOS) - repository: code cleanups / simplifications - internal crypto api: speedups / cleanups / refactorings / modernisation - remove "borg upgrade" support for "attic backup" repos - remove PassphraseKey code and borg key migrate-to-repokey command - OpenBSD: build borg with OpenSSL (not: LibreSSL), #6474 - remove support for LibreSSL, #6474 - remove support for OpenSSL < 1.1.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/changes_0.x.rst0000644000076500000240000010066214716225054016306 0ustar00twstaff.. _changelog_0x: Change Log 0.x ============== Version 0.30.0 (2016-01-23) --------------------------- Compatibility notes: - The new default logging level is WARNING. Previously, it was INFO, which was more verbose. Use -v (or --info) to show once again log level INFO messages. See the "general" section in the usage docs. - for borg create, you need --list (additionally to -v) to see the long file list (was needed so you can have e.g. --stats alone without the long list) - see below about BORG_DELETE_I_KNOW_WHAT_I_AM_DOING (was: BORG_CHECK_I_KNOW_WHAT_I_AM_DOING) Bug fixes: - fix crash when using borg create --dry-run --keep-tag-files, #570 - make sure teardown with cleanup happens for Cache and RepositoryCache, avoiding leftover locks and TEMP dir contents, #285 (partially), #548 - fix locking KeyError, partial fix for #502 - log stats consistently, #526 - add abbreviated weekday to timestamp format, fixes #496 - strip whitespace when loading exclusions from file - unset LD_LIBRARY_PATH before invoking ssh, fixes strange OpenSSL library version warning when using the borg binary, #514 - add some error handling/fallback for C library loading, #494 - added BORG_DELETE_I_KNOW_WHAT_I_AM_DOING for check in "borg delete", #503 - remove unused "repair" rpc method name New features: - borg create: implement exclusions using regular expression patterns. - borg create: implement inclusions using patterns. - borg extract: support patterns, #361 - support different styles for patterns: - fnmatch (`fm:` prefix, default when omitted), like borg <= 0.29. - shell (`sh:` prefix) with `*` not matching directory separators and `**/` matching 0..n directories - path prefix (`pp:` prefix, for unifying borg create pp1 pp2 into the patterns system), semantics like in borg <= 0.29 - regular expression (`re:`), new! - --progress option for borg upgrade (#291) and borg delete - update progress indication more often (e.g. for borg create within big files or for borg check repo), #500 - finer chunker granularity for items metadata stream, #547, #487 - borg create --list now used (additionally to -v) to enable the verbose file list output - display borg version below tracebacks, #532 Other changes: - hashtable size (and thus: RAM and disk consumption) follows a growth policy: grows fast while small, grows slower when getting bigger, #527 - Vagrantfile: use pyinstaller 3.1 to build binaries, freebsd sqlite3 fix, fixes #569 - no separate binaries for centos6 any more because the generic linux binaries also work on centos6 (or in general: on systems with a slightly older glibc than debian7 - dev environment: require virtualenv<14.0 so we get a py32 compatible pip - docs: - add space-saving chunks.archive.d trick to FAQ - important: clarify -v and log levels in usage -> general, please read! - sphinx configuration: create a simple man page from usage docs - add a repo server setup example - disable unneeded SSH features in authorized_keys examples for security. - borg prune only knows "--keep-within" and not "--within" - add gource video to resources docs, #507 - add netbsd install instructions - authors: make it more clear what refers to borg and what to attic - document standalone binary requirements, #499 - rephrase the mailing list section - development docs: run build_api and build_usage before tagging release - internals docs: hash table max. load factor is 0.75 now - markup, typo, grammar, phrasing, clarifications and other fixes. - add gcc gcc-c++ to redhat/fedora/corora install docs, fixes #583 Version 0.29.0 (2015-12-13) --------------------------- Compatibility notes: - when upgrading to 0.29.0 you need to upgrade client as well as server installations due to the locking and commandline interface changes otherwise you'll get an error msg about a RPC protocol mismatch or a wrong commandline option. if you run a server that needs to support both old and new clients, it is suggested that you have a "borg-0.28.2" and a "borg-0.29.0" command. clients then can choose via e.g. "borg --remote-path=borg-0.29.0 ...". - the default waiting time for a lock changed from infinity to 1 second for a better interactive user experience. if the repo you want to access is currently locked, borg will now terminate after 1s with an error message. if you have scripts that shall wait for the lock for a longer time, use --lock-wait N (with N being the maximum wait time in seconds). Bug fixes: - hash table tuning (better chosen hashtable load factor 0.75 and prime initial size of 1031 gave ~1000x speedup in some scenarios) - avoid creation of an orphan lock for one case, #285 - --keep-tag-files: fix file mode and multiple tag files in one directory, #432 - fixes for "borg upgrade" (attic repo converter), #466 - remove --progress isatty magic (and also --no-progress option) again, #476 - borg init: display proper repo URL - fix format of umask in help pages, #463 New features: - implement --lock-wait, support timeout for UpgradableLock, #210 - implement borg break-lock command, #157 - include system info below traceback, #324 - sane remote logging, remote stderr, #461: - remote log output: intercept it and log it via local logging system, with "Remote: " prefixed to message. log remote tracebacks. - remote stderr: output it to local stderr with "Remote: " prefixed. - add --debug and --info (same as --verbose) to set the log level of the builtin logging configuration (which otherwise defaults to warning), #426 note: there are few messages emitted at DEBUG level currently. - optionally configure logging via env var BORG_LOGGING_CONF - add --filter option for status characters: e.g. to show only the added or modified files (and also errors), use "borg create -v --filter=AME ...". - more progress indicators, #394 - use ISO-8601 date and time format, #375 - "borg check --prefix" to restrict archive checking to that name prefix, #206 Other changes: - hashindex_add C implementation (speed up cache re-sync for new archives) - increase FUSE read_size to 1024 (speed up metadata operations) - check/delete/prune --save-space: free unused segments quickly, #239 - increase rpc protocol version to 2 (see also Compatibility notes), #458 - silence borg by default (via default log level WARNING) - get rid of C compiler warnings, #391 - upgrade OS X FUSE to 3.0.9 on the OS X binary build system - use python 3.5.1 to build binaries - docs: - new mailing list borgbackup@python.org, #468 - readthedocs: color and logo improvements - load coverage icons over SSL (avoids mixed content) - more precise binary installation steps - update release procedure docs about OS X FUSE - FAQ entry about unexpected 'A' status for unchanged file(s), #403 - add docs about 'E' file status - add "borg upgrade" docs, #464 - add developer docs about output and logging - clarify encryption, add note about client-side encryption - add resources section, with videos, talks, presentations, #149 - Borg moved to Arch Linux [community] - fix wrong installation instructions for archlinux Version 0.28.2 (2015-11-15) --------------------------- New features: - borg create --exclude-if-present TAGFILE - exclude directories that have the given file from the backup. You can additionally give --keep-tag-files to preserve just the directory roots and the tag-files (but not back up other directory contents), #395, attic #128, attic #142 Other changes: - do not create docs sources at build time (just have them in the repo), completely remove have_cython() hack, do not use the "mock" library at build time, #384 - avoid hidden import, make it easier for PyInstaller, easier fix for #218 - docs: - add description of item flags / status output, fixes #402 - explain how to regenerate usage and API files (build_api or build_usage) and when to commit usage files directly into git, #384 - minor install docs improvements Version 0.28.1 (2015-11-08) --------------------------- Bug fixes: - do not try to build api / usage docs for production install, fixes unexpected "mock" build dependency, #384 Other changes: - avoid using msgpack.packb at import time - fix formatting issue in changes.rst - fix build on readthedocs Version 0.28.0 (2015-11-08) --------------------------- Compatibility notes: - changed return codes (exit codes), see docs. in short: old: 0 = ok, 1 = error. now: 0 = ok, 1 = warning, 2 = error New features: - refactor return codes (exit codes), fixes #61 - add --show-rc option enable "terminating with X status, rc N" output, fixes 58, #351 - borg create backups atime and ctime additionally to mtime, fixes #317 - extract: support atime additionally to mtime - FUSE: support ctime and atime additionally to mtime - support borg --version - emit a warning if we have a slow msgpack installed - borg list --prefix=thishostname- REPO, fixes #205 - Debug commands (do not use except if you know what you do: debug-get-obj, debug-put-obj, debug-delete-obj, debug-dump-archive-items. Bug fixes: - setup.py: fix bug related to BORG_LZ4_PREFIX processing - fix "check" for repos that have incomplete chunks, fixes #364 - borg mount: fix unlocking of repository at umount time, fixes #331 - fix reading files without touching their atime, #334 - non-ascii ACL fixes for Linux, FreeBSD and OS X, #277 - fix acl_use_local_uid_gid() and add a test for it, attic #359 - borg upgrade: do not upgrade repositories in place by default, #299 - fix cascading failure with the index conversion code, #269 - borg check: implement 'cmdline' archive metadata value decoding, #311 - fix RobustUnpacker, it missed some metadata keys (new atime and ctime keys were missing, but also bsdflags). add check for unknown metadata keys. - create from stdin: also save atime, ctime (cosmetic) - use default_notty=False for confirmations, fixes #345 - vagrant: fix msgpack installation on centos, fixes #342 - deal with unicode errors for symlinks in same way as for regular files and have a helpful warning message about how to fix wrong locale setup, fixes #382 - add ACL keys the RobustUnpacker must know about Other changes: - improve file size displays, more flexible size formatters - explicitly commit to the units standard, #289 - archiver: add E status (means that an error occurred when processing this (single) item - do binary releases via "github releases", closes #214 - create: use -x and --one-file-system (was: --do-not-cross-mountpoints), #296 - a lot of changes related to using "logging" module and screen output, #233 - show progress display if on a tty, output more progress information, #303 - factor out status output so it is consistent, fix surrogates removal, maybe fixes #309 - move away from RawConfigParser to ConfigParser - archive checker: better error logging, give chunk_id and sequence numbers (can be used together with borg debug-dump-archive-items). - do not mention the deprecated passphrase mode - emit a deprecation warning for --compression N (giving a just a number) - misc .coverragerc fixes (and coverage measurement improvements), fixes #319 - refactor confirmation code, reduce code duplication, add tests - prettier error messages, fixes #307, #57 - tests: - add a test to find disk-full issues, #327 - travis: also run tests on Python 3.5 - travis: use tox -r so it rebuilds the tox environments - test the generated pyinstaller-based binary by archiver unit tests, #215 - vagrant: tests: announce whether fakeroot is used or not - vagrant: add vagrant user to fuse group for debianoid systems also - vagrant: llfuse install on darwin needs pkgconfig installed - vagrant: use pyinstaller from develop branch, fixes #336 - benchmarks: test create, extract, list, delete, info, check, help, fixes #146 - benchmarks: test with both the binary and the python code - archiver tests: test with both the binary and the python code, fixes #215 - make basic test more robust - docs: - moved docs to borgbackup.readthedocs.org, #155 - a lot of fixes and improvements, use mobile-friendly RTD standard theme - use zlib,6 compression in some examples, fixes #275 - add missing rename usage to docs, closes #279 - include the help offered by borg help in the usage docs, fixes #293 - include a list of major changes compared to attic into README, fixes #224 - add OS X install instructions, #197 - more details about the release process, #260 - fix linux glibc requirement (binaries built on debian7 now) - build: move usage and API generation to setup.py - update docs about return codes, #61 - remove api docs (too much breakage on rtd) - borgbackup install + basics presentation (asciinema) - describe the current style guide in documentation - add section about debug commands - warn about not running out of space - add example for rename - improve chunker params docs, fixes #362 - minor development docs update Version 0.27.0 (2015-10-07) --------------------------- New features: - "borg upgrade" command - attic -> borg one time converter / migration, #21 - temporary hack to avoid using lots of disk space for chunks.archive.d, #235: To use it: rm -rf chunks.archive.d ; touch chunks.archive.d - respect XDG_CACHE_HOME, attic #181 - add support for arbitrary SSH commands, attic #99 - borg delete --cache-only REPO (only delete cache, not REPO), attic #123 Bug fixes: - use Debian 7 (wheezy) to build pyinstaller borgbackup binaries, fixes slow down observed when running the Centos6-built binary on Ubuntu, #222 - do not crash on empty lock.roster, fixes #232 - fix multiple issues with the cache config version check, #234 - fix segment entry header size check, attic #352 plus other error handling improvements / code deduplication there. - always give segment and offset in repo IntegrityErrors Other changes: - stop producing binary wheels, remove docs about it, #147 - docs: - add warning about prune - generate usage include files only as needed - development docs: add Vagrant section - update / improve / reformat FAQ - hint to single-file pyinstaller binaries from README Version 0.26.1 (2015-09-28) --------------------------- This is a minor update, just docs and new pyinstaller binaries. - docs update about python and binary requirements - better docs for --read-special, fix #220 - re-built the binaries, fix #218 and #213 (glibc version issue) - update web site about single-file pyinstaller binaries Note: if you did a python-based installation, there is no need to upgrade. Version 0.26.0 (2015-09-19) --------------------------- New features: - Faster cache sync (do all in one pass, remove tar/compression stuff), #163 - BORG_REPO env var to specify the default repo, #168 - read special files as if they were regular files, #79 - implement borg create --dry-run, attic issue #267 - Normalize paths before pattern matching on OS X, #143 - support OpenBSD and NetBSD (except xattrs/ACLs) - support / run tests on Python 3.5 Bug fixes: - borg mount repo: use absolute path, attic #200, attic #137 - chunker: use off_t to get 64bit on 32bit platform, #178 - initialize chunker fd to -1, so it's not equal to STDIN_FILENO (0) - fix reaction to "no" answer at delete repo prompt, #182 - setup.py: detect lz4.h header file location - to support python < 3.2.4, add less buggy argparse lib from 3.2.6 (#194) - fix for obtaining ``char *`` from temporary Python value (old code causes a compile error on Mint 17.2) - llfuse 0.41 install troubles on some platforms, require < 0.41 (UnicodeDecodeError exception due to non-ascii llfuse setup.py) - cython code: add some int types to get rid of unspecific python add / subtract operations (avoid ``undefined symbol FPE_``... error on some platforms) - fix verbose mode display of stdin backup - extract: warn if a include pattern never matched, fixes #209, implement counters for Include/ExcludePatterns - archive names with slashes are invalid, attic issue #180 - chunker: add a check whether the POSIX_FADV_DONTNEED constant is defined - fixes building on OpenBSD. Other changes: - detect inconsistency / corruption / hash collision, #170 - replace versioneer with setuptools_scm, #106 - docs: - pkg-config is needed for llfuse installation - be more clear about pruning, attic issue #132 - unit tests: - xattr: ignore security.selinux attribute showing up - ext3 seems to need a bit more space for a sparse file - do not test lzma level 9 compression (avoid MemoryError) - work around strange mtime granularity issue on netbsd, fixes #204 - ignore st_rdev if file is not a block/char device, fixes #203 - stay away from the setgid and sticky mode bits - use Vagrant to do easy cross-platform testing (#196), currently: - Debian 7 "wheezy" 32bit, Debian 8 "jessie" 64bit - Ubuntu 12.04 32bit, Ubuntu 14.04 64bit - Centos 7 64bit - FreeBSD 10.2 64bit - OpenBSD 5.7 64bit - NetBSD 6.1.5 64bit - Darwin (OS X Yosemite) Version 0.25.0 (2015-08-29) --------------------------- Compatibility notes: - lz4 compression library (liblz4) is a new requirement (#156) - the new compression code is very compatible: as long as you stay with zlib compression, older borg releases will still be able to read data from a repo/archive made with the new code (note: this is not the case for the default "none" compression, use "zlib,0" if you want a "no compression" mode that can be read by older borg). Also the new code is able to read repos and archives made with older borg versions (for all zlib levels 0..9). Deprecations: - --compression N (with N being a number, as in 0.24) is deprecated. We keep the --compression 0..9 for now not to break scripts, but it is deprecated and will be removed later, so better fix your scripts now: --compression 0 (as in 0.24) is the same as --compression zlib,0 (now). BUT: if you do not want compression, use --compression none (which is the default). --compression 1 (in 0.24) is the same as --compression zlib,1 (now) --compression 9 (in 0.24) is the same as --compression zlib,9 (now) New features: - create --compression none (default, means: do not compress, just pass through data "as is". this is more efficient than zlib level 0 as used in borg 0.24) - create --compression lz4 (super-fast, but not very high compression) - create --compression zlib,N (slower, higher compression, default for N is 6) - create --compression lzma,N (slowest, highest compression, default N is 6) - honor the nodump flag (UF_NODUMP) and do not back up such items - list --short just outputs a simple list of the files/directories in an archive Bug fixes: - fixed --chunker-params parameter order confusion / malfunction, fixes #154 - close fds of segments we delete (during compaction) - close files which fell out the lrucache - fadvise DONTNEED now is only called for the byte range actually read, not for the whole file, fixes #158. - fix issue with negative "all archives" size, fixes #165 - restore_xattrs: ignore if setxattr fails with EACCES, fixes #162 Other changes: - remove fakeroot requirement for tests, tests run faster without fakeroot (test setup does not fail any more without fakeroot, so you can run with or without fakeroot), fixes #151 and #91. - more tests for archiver - recover_segment(): don't assume we have an fd for segment - lrucache refactoring / cleanup, add dispose function, py.test tests - generalize hashindex code for any key length (less hardcoding) - lock roster: catch file not found in remove() method and ignore it - travis CI: use requirements file - improved docs: - replace hack for llfuse with proper solution (install libfuse-dev) - update docs about compression - update development docs about fakeroot - internals: add some words about lock files / locking system - support: mention BountySource and for what it can be used - theme: use a lighter green - add pypi, wheel, dist package based install docs - split install docs into system-specific preparations and generic instructions Version 0.24.0 (2015-08-09) --------------------------- Incompatible changes (compared to 0.23): - borg now always issues --umask NNN option when invoking another borg via ssh on the repository server. By that, it's making sure it uses the same umask for remote repos as for local ones. Because of this, you must upgrade both server and client(s) to 0.24. - the default umask is 077 now (if you do not specify via --umask) which might be a different one as you used previously. The default umask avoids that you accidentally give access permissions for group and/or others to files created by borg (e.g. the repository). Deprecations: - "--encryption passphrase" mode is deprecated, see #85 and #97. See the new "--encryption repokey" mode for a replacement. New features: - borg create --chunker-params ... to configure the chunker, fixes #16 (attic #302, attic #300, and somehow also #41). This can be used to reduce memory usage caused by chunk management overhead, so borg does not create a huge chunks index/repo index and eats all your RAM if you back up lots of data in huge files (like VM disk images). See docs/misc/create_chunker-params.txt for more information. - borg info now reports chunk counts in the chunk index. - borg create --compression 0..9 to select zlib compression level, fixes #66 (attic #295). - borg init --encryption repokey (to store the encryption key into the repo), fixes #85 - improve at-end error logging, always log exceptions and set exit_code=1 - LoggedIO: better error checks / exceptions / exception handling - implement --remote-path to allow non-default-path borg locations, #125 - implement --umask M and use 077 as default umask for better security, #117 - borg check: give a named single archive to it, fixes #139 - cache sync: show progress indication - cache sync: reimplement the chunk index merging in C Bug fixes: - fix segfault that happened for unreadable files (chunker: n needs to be a signed size_t), #116 - fix the repair mode, #144 - repo delete: add destroy to allowed rpc methods, fixes issue #114 - more compatible repository locking code (based on mkdir), maybe fixes #92 (attic #317, attic #201). - better Exception msg if no Borg is installed on the remote repo server, #56 - create a RepositoryCache implementation that can cope with >2GiB, fixes attic #326. - fix Traceback when running check --repair, attic #232 - clarify help text, fixes #73. - add help string for --no-files-cache, fixes #140 Other changes: - improved docs: - added docs/misc directory for misc. writeups that won't be included "as is" into the html docs. - document environment variables and return codes (attic #324, attic #52) - web site: add related projects, fix web site url, IRC #borgbackup - Fedora/Fedora-based install instructions added to docs - Cygwin-based install instructions added to docs - updated AUTHORS - add FAQ entries about redundancy / integrity - clarify that borg extract uses the cwd as extraction target - update internals doc about chunker params, memory usage and compression - added docs about development - add some words about resource usage in general - document how to back up a raw disk - add note about how to run borg from virtual env - add solutions for (ll)fuse installation problems - document what borg check does, fixes #138 - reorganize borgbackup.github.io sidebar, prev/next at top - deduplicate and refactor the docs / README.rst - use borg-tmp as prefix for temporary files / directories - short prune options without "keep-" are deprecated, do not suggest them - improved tox configuration - remove usage of unittest.mock, always use mock from pypi - use entrypoints instead of scripts, for better use of the wheel format and modern installs - add requirements.d/development.txt and modify tox.ini - use travis-ci for testing based on Linux and (new) OS X - use coverage.py, pytest-cov and codecov.io for test coverage support I forgot to list some stuff already implemented in 0.23.0, here they are: New features: - efficient archive list from manifest, meaning a big speedup for slow repo connections and "list ", "delete ", "prune" (attic #242, attic #167) - big speedup for chunks cache sync (esp. for slow repo connections), fixes #18 - hashindex: improve error messages Other changes: - explicitly specify binary mode to open binary files - some easy micro optimizations Version 0.23.0 (2015-06-11) --------------------------- Incompatible changes (compared to attic, fork related): - changed sw name and cli command to "borg", updated docs - package name (and name in urls) uses "borgbackup" to have fewer collisions - changed repo / cache internal magic strings from ATTIC* to BORG*, changed cache location to .cache/borg/ - this means that it currently won't accept attic repos (see issue #21 about improving that) Bug fixes: - avoid defect python-msgpack releases, fixes attic #171, fixes attic #185 - fix traceback when trying to do unsupported passphrase change, fixes attic #189 - datetime does not like the year 10.000, fixes attic #139 - fix "info" all archives stats, fixes attic #183 - fix parsing with missing microseconds, fixes attic #282 - fix misleading hint the fuse ImportError handler gave, fixes attic #237 - check unpacked data from RPC for tuple type and correct length, fixes attic #127 - fix Repository._active_txn state when lock upgrade fails - give specific path to xattr.is_enabled(), disable symlink setattr call that always fails - fix test setup for 32bit platforms, partial fix for attic #196 - upgraded versioneer, PEP440 compliance, fixes attic #257 New features: - less memory usage: add global option --no-cache-files - check --last N (only check the last N archives) - check: sort archives in reverse time order - rename repo::oldname newname (rename repository) - create -v output more informative - create --progress (backup progress indicator) - create --timestamp (utc string or reference file/dir) - create: if "-" is given as path, read binary from stdin - extract: if --stdout is given, write all extracted binary data to stdout - extract --sparse (simple sparse file support) - extra debug information for 'fread failed' - delete (deletes whole repo + local cache) - FUSE: reflect deduplication in allocated blocks - only allow whitelisted RPC calls in server mode - normalize source/exclude paths before matching - use posix_fadvise not to spoil the OS cache, fixes attic #252 - toplevel error handler: show tracebacks for better error analysis - sigusr1 / sigint handler to print current file infos - attic PR #286 - RPCError: include the exception args we get from remote Other changes: - source: misc. cleanups, pep8, style - docs and faq improvements, fixes, updates - cleanup crypto.pyx, make it easier to adapt to other AES modes - do os.fsync like recommended in the python docs - source: Let chunker optionally work with os-level file descriptor. - source: Linux: remove duplicate os.fsencode calls - source: refactor _open_rb code a bit, so it is more consistent / regular - source: refactor indicator (status) and item processing - source: use py.test for better testing, flake8 for code style checks - source: fix tox >=2.0 compatibility (test runner) - pypi package: add python version classifiers, add FreeBSD to platforms Attic Changelog --------------- Here you can see the full list of changes between each Attic release until Borg forked from Attic: Version 0.17 ~~~~~~~~~~~~ (bugfix release, released on X) - Fix hashindex ARM memory alignment issue (#309) - Improve hashindex error messages (#298) Version 0.16 ~~~~~~~~~~~~ (bugfix release, released on May 16, 2015) - Fix typo preventing the security confirmation prompt from working (#303) - Improve handling of systems with improperly configured file system encoding (#289) - Fix "All archives" output for attic info. (#183) - More user friendly error message when repository key file is not found (#236) - Fix parsing of iso 8601 timestamps with zero microseconds (#282) Version 0.15 ~~~~~~~~~~~~ (bugfix release, released on Apr 15, 2015) - xattr: Be less strict about unknown/unsupported platforms (#239) - Reduce repository listing memory usage (#163). - Fix BrokenPipeError for remote repositories (#233) - Fix incorrect behavior with two character directory names (#265, #268) - Require approval before accessing relocated/moved repository (#271) - Require approval before accessing previously unknown unencrypted repositories (#271) - Fix issue with hash index files larger than 2GB. - Fix Python 3.2 compatibility issue with noatime open() (#164) - Include missing pyx files in dist files (#168) Version 0.14 ~~~~~~~~~~~~ (feature release, released on Dec 17, 2014) - Added support for stripping leading path segments (#95) "attic extract --strip-segments X" - Add workaround for old Linux systems without acl_extended_file_no_follow (#96) - Add MacPorts' path to the default openssl search path (#101) - HashIndex improvements, eliminates unnecessary IO on low memory systems. - Fix "Number of files" output for attic info. (#124) - limit create file permissions so files aren't read while restoring - Fix issue with empty xattr values (#106) Version 0.13 ~~~~~~~~~~~~ (feature release, released on Jun 29, 2014) - Fix sporadic "Resource temporarily unavailable" when using remote repositories - Reduce file cache memory usage (#90) - Faster AES encryption (utilizing AES-NI when available) - Experimental Linux, OS X and FreeBSD ACL support (#66) - Added support for backup and restore of BSDFlags (OSX, FreeBSD) (#56) - Fix bug where xattrs on symlinks were not correctly restored - Added cachedir support. CACHEDIR.TAG compatible cache directories can now be excluded using ``--exclude-caches`` (#74) - Fix crash on extreme mtime timestamps (year 2400+) (#81) - Fix Python 3.2 specific lockf issue (EDEADLK) Version 0.12 ~~~~~~~~~~~~ (feature release, released on April 7, 2014) - Python 3.4 support (#62) - Various documentation improvements a new style - ``attic mount`` now supports mounting an entire repository not only individual archives (#59) - Added option to restrict remote repository access to specific path(s): ``attic serve --restrict-to-path X`` (#51) - Include "all archives" size information in "--stats" output. (#54) - Added ``--stats`` option to ``attic delete`` and ``attic prune`` - Fixed bug where ``attic prune`` used UTC instead of the local time zone when determining which archives to keep. - Switch to SI units (Power of 1000 instead 1024) when printing file sizes Version 0.11 ~~~~~~~~~~~~ (feature release, released on March 7, 2014) - New "check" command for repository consistency checking (#24) - Documentation improvements - Fix exception during "attic create" with repeated files (#39) - New "--exclude-from" option for attic create/extract/verify. - Improved archive metadata deduplication. - "attic verify" has been deprecated. Use "attic extract --dry-run" instead. - "attic prune --hourly|daily|..." has been deprecated. Use "attic prune --keep-hourly|daily|..." instead. - Ignore xattr errors during "extract" if not supported by the filesystem. (#46) Version 0.10 ~~~~~~~~~~~~ (bugfix release, released on Jan 30, 2014) - Fix deadlock when extracting 0 sized files from remote repositories - "--exclude" wildcard patterns are now properly applied to the full path not just the file name part (#5). - Make source code endianness agnostic (#1) Version 0.9 ~~~~~~~~~~~ (feature release, released on Jan 23, 2014) - Remote repository speed and reliability improvements. - Fix sorting of segment names to ignore NFS left over files. (#17) - Fix incorrect display of time (#13) - Improved error handling / reporting. (#12) - Use fcntl() instead of flock() when locking repository/cache. (#15) - Let ssh figure out port/user if not specified so we don't override .ssh/config (#9) - Improved libcrypto path detection (#23). Version 0.8.1 ~~~~~~~~~~~~~ (bugfix release, released on Oct 4, 2013) - Fix segmentation fault issue. Version 0.8 ~~~~~~~~~~~ (feature release, released on Oct 3, 2013) - Fix xattr issue when backing up sshfs filesystems (#4) - Fix issue with excessive index file size (#6) - Support access of read only repositories. - New syntax to enable repository encryption: attic init --encryption="none|passphrase|keyfile". - Detect and abort if repository is older than the cache. Version 0.7 ~~~~~~~~~~~ (feature release, released on Aug 5, 2013) - Ported to FreeBSD - Improved documentation - Experimental: Archives mountable as FUSE filesystems. - The "user." prefix is no longer stripped from xattrs on Linux Version 0.6.1 ~~~~~~~~~~~~~ (bugfix release, released on July 19, 2013) - Fixed an issue where mtime was not always correctly restored. Version 0.6 ~~~~~~~~~~~ First public release on July 9, 2013 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/changes_1.x.rst0000644000076500000240000061017714716225054016316 0ustar00twstaff.. _important_notes_1x: Important notes 1.x =================== This section provides information about security and corruption issues. .. _archives_tam_vuln: Pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811) ---------------------------------------------------------- A flaw in the cryptographic authentication scheme in Borg allowed an attacker to fake archives and potentially indirectly cause backup data loss in the repository. The attack requires an attacker to be able to 1. insert files (with no additional headers) into backups 2. gain write access to the repository This vulnerability does not disclose plaintext to the attacker, nor does it affect the authenticity of existing archives. Creating plausible fake archives may be feasible for empty or small archives, but is unlikely for large archives. The fix enforces checking the TAM authentication tag of archives at critical places. Borg now considers archives without TAM as garbage or an attack. We are not aware of others having discovered, disclosed or exploited this vulnerability. Below, if we speak of borg 1.2.6, we mean a borg version >= 1.2.6 **or** a borg version that has the relevant security patches for this vulnerability applied (could be also an older version in that case). Steps you must take to upgrade a repository (this applies to all kinds of repos no matter what encryption mode they use, including "none"): 1. Upgrade all clients using this repository to borg 1.2.6. Note: it is not required to upgrade a server, except if the server-side borg is also used as a client (and not just for "borg serve"). Do **not** run ``borg check`` with borg 1.2.6 before completing the upgrade steps: - ``borg check`` would complain about archives without a valid archive TAM. - ``borg check --repair`` would remove such archives! 2. Run: ``BORG_WORKAROUNDS=ignore_invalid_archive_tam borg info --debug 2>&1 | grep TAM | grep -i manifest`` a) If you get "TAM-verified manifest", continue with 3. b) If you get "Manifest TAM not found and not required", run ``borg upgrade --tam --force `` *on every client*. 3. Run: ``BORG_WORKAROUNDS=ignore_invalid_archive_tam borg list --consider-checkpoints --format='{name} {time} tam:{tam}{NL}' `` "tam:verified" means that the archive has a valid TAM authentication. "tam:none" is expected as output for archives created by borg <1.0.9. "tam:none" is also expected for archives resulting from a borg rename or borg recreate operation (see #7791). "tam:none" could also come from archives created by an attacker. You should verify that "tam:none" archives are authentic and not malicious (== have good content, have correct timestamp, can be extracted successfully). In case you find crappy/malicious archives, you must delete them before proceeding. In low-risk, trusted environments, you may decide on your own risk to skip step 3 and just trust in everything being OK. 4. If there are no tam:none archives left at this point, you can skip this step. Run ``BORG_WORKAROUNDS=ignore_invalid_archive_tam borg upgrade --archives-tam ``. This will unconditionally add a correct archive TAM to all archives not having one. ``borg check`` would consider TAM-less or invalid-TAM archives as garbage or a potential attack. To see that all archives now are "tam:verified" run: ``borg list --consider-checkpoints --format='{name} {time} tam:{tam}{NL}' `` 5. Please note that you should never use BORG_WORKAROUNDS=ignore_invalid_archive_tam for normal production operations - it is only needed once to get the archives in a repository into a good state. All archives have a valid TAM now. Vulnerability time line: * 2023-06-13: Vulnerability discovered during code review by Thomas Waldmann * 2023-06-13...: Work on fixing the issue, upgrade procedure, docs. * 2023-06-30: CVE was assigned via Github CNA * 2023-06-30 .. 2023-08-29: Fixed issue, code review, docs, testing. * 2023-08-30: Released fixed version 1.2.5 (broken upgrade procedure for some repos) * 2023-08-31: Released fixed version 1.2.6 (fixes upgrade procedure) .. _hashindex_set_bug: Pre-1.1.11 potential index corruption / data loss issue ------------------------------------------------------- A bug was discovered in our hashtable code, see issue #4829. The code is used for the client-side chunks cache and the server-side repo index. Although borg uses the hashtables very heavily, the index corruption did not happen too frequently, because it needed specific conditions to happen. Data loss required even more specific conditions, so it should be rare (and also detectable via borg check). You might be affected if borg crashed with / complained about: - AssertionError: Corrupted segment reference count - corrupted index or hints - ObjectNotFound: Object with key ... not found in repository ... - Index mismatch for key b'...'. (..., ...) != (-1, -1) - ValueError: stats_against: key contained in self but not in master_index. Advised procedure to fix any related issue in your indexes/caches: - install fixed borg code (on client AND server) - for all of your clients and repos remove the cache by: borg delete --cache-only YOURREPO (later, the cache will be re-built automatically) - for all your repos, rebuild the repo index by: borg check --repair YOURREPO This will also check all archives and detect if there is any data-loss issue. Affected branches / releases: - fd06497 introduced the bug into 1.1-maint branch - it affects all borg 1.1.x since 1.1.0b4. - fd06497 introduced the bug into master branch - it affects all borg 1.2.0 alpha releases. - c5cd882 introduced the bug into 1.0-maint branch - it affects all borg 1.0.x since 1.0.11rc1. The bug was fixed by: - 701159a fixes the bug in 1.1-maint branch - will be released with borg 1.1.11. - fa63150 fixes the bug in master branch - will be released with borg 1.2.0a8. - 7bb90b6 fixes the bug in 1.0-maint branch. Branch is EOL, no new release is planned as of now. .. _broken_validator: Pre-1.1.4 potential data corruption issue ----------------------------------------- A data corruption bug was discovered in borg check --repair, see issue #3444. This is a 1.1.x regression, releases < 1.1 (e.g. 1.0.x) are not affected. To avoid data loss, you must not run borg check --repair using an unfixed version of borg 1.1.x. The first official release that has the fix is 1.1.4. Package maintainers may have applied the fix to updated packages of 1.1.x (x<4) though, see the package maintainer's package changelog to make sure. If you never had missing item metadata chunks, the bug has not affected you even if you did run borg check --repair with an unfixed version. When borg check --repair tried to repair corrupt archives that miss item metadata chunks, the resync to valid metadata in still present item metadata chunks malfunctioned. This was due to a broken validator that considered all (even valid) item metadata as invalid. As they were considered invalid, borg discarded them. Practically, that means the affected files, directories or other fs objects were discarded from the archive. Due to the malfunction, the process was extremely slow, but if you let it complete, borg would have created a "repaired" archive that has lost a lot of items. If you interrupted borg check --repair because it was so strangely slow (killing borg somehow, e.g. Ctrl-C) the transaction was rolled back and no corruption occurred. The log message indicating the precondition for the bug triggering looks like: item metadata chunk missing [chunk: 001056_bdee87d...a3e50d] If you never had that in your borg check --repair runs, you're not affected. But if you're unsure or you actually have seen that, better check your archives. By just using "borg list repo::archive" you can see if all expected filesystem items are listed. .. _tam_vuln: Pre-1.0.9 manifest spoofing vulnerability (CVE-2016-10099) ---------------------------------------------------------- A flaw in the cryptographic authentication scheme in Borg allowed an attacker to spoof the manifest. The attack requires an attacker to be able to 1. insert files (with no additional headers) into backups 2. gain write access to the repository This vulnerability does not disclose plaintext to the attacker, nor does it affect the authenticity of existing archives. The vulnerability allows an attacker to create a spoofed manifest (the list of archives). Creating plausible fake archives may be feasible for small archives, but is unlikely for large archives. The fix adds a separate authentication tag to the manifest. For compatibility with prior versions this authentication tag is *not* required by default for existing repositories. Repositories created with 1.0.9 and later require it. Steps you should take: 1. Upgrade all clients to 1.0.9 or later. 2. Run ``borg upgrade --tam `` *on every client* for *each* repository. 3. This will list all archives, including archive IDs, for easy comparison with your logs. 4. Done. Prior versions can access and modify repositories with this measure enabled, however, to 1.0.9 or later their modifications are indiscernible from an attack and will raise an error until the below procedure is followed. We are aware that this can be annoying in some circumstances, but don't see a way to fix the vulnerability otherwise. In case a version prior to 1.0.9 is used to modify a repository where above procedure was completed, and now you get an error message from other clients: 1. ``borg upgrade --tam --force `` once with *any* client suffices. This attack is mitigated by: - Noting/logging ``borg list``, ``borg info``, or ``borg create --stats``, which contain the archive IDs. We are not aware of others having discovered, disclosed or exploited this vulnerability. Vulnerability time line: * 2016-11-14: Vulnerability and fix discovered during review of cryptography by Marian Beermann (@enkore) * 2016-11-20: First patch * 2016-12-20: Released fixed version 1.0.9 * 2017-01-02: CVE was assigned * 2017-01-15: Released fixed version 1.1.0b3 (fix was previously only available from source) .. _attic013_check_corruption: Pre-1.0.9 potential data loss ----------------------------- If you have archives in your repository that were made with attic <= 0.13 (and later migrated to borg), running borg check would report errors in these archives. See issue #1837. The reason for this is a invalid (and useless) metadata key that was always added due to a bug in these old attic versions. If you run borg check --repair, things escalate quickly: all archive items with invalid metadata will be killed. Due to that attic bug, that means all items in all archives made with these old attic versions. Pre-1.0.4 potential repo corruption ----------------------------------- Some external errors (like network or disk I/O errors) could lead to corruption of the backup repository due to issue #1138. A sign that this happened is if "E" status was reported for a file that can not be explained by problems with the source file. If you still have logs from "borg create -v --list", you can check for "E" status. Here is what could cause corruption and what you can do now: 1) I/O errors (e.g. repo disk errors) while writing data to repo. This could lead to corrupted segment files. Fix:: # check for corrupt chunks / segments: borg check -v --repository-only REPO # repair the repo: borg check -v --repository-only --repair REPO # make sure everything is fixed: borg check -v --repository-only REPO 2) Unreliable network / unreliable connection to the repo. This could lead to archive metadata corruption. Fix:: # check for corrupt archives: borg check -v --archives-only REPO # delete the corrupt archives: borg delete --force REPO::CORRUPT_ARCHIVE # make sure everything is fixed: borg check -v --archives-only REPO 3) In case you want to do more intensive checking. The best check that everything is ok is to run a dry-run extraction:: borg extract -v --dry-run REPO::ARCHIVE .. _upgradenotes: Upgrade Notes ============= borg 1.1.x to 1.2.x ------------------- Some things can be recommended for the upgrade process from borg 1.1.x (please also read the important compatibility notes below): - first upgrade to a recent 1.1.x release - especially if you run some older 1.1.* or even 1.0.* borg release. - using that, run at least one `borg create` (your normal backup), `prune` and especially a `check` to see everything is in a good state. - check the output of `borg check` - if there is anything special, consider a `borg check --repair` followed by another `borg check`. - if everything is fine so far (borg check reports no issues), you can consider upgrading to 1.2.x. if not, please first fix any already existing issue. - if you want to play safer, first **create a backup of your borg repository**. - upgrade to latest borg 1.2.x release (you could use the fat binary from github releases page) - borg 1.2.6 has a security fix for the pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), see details and necessary upgrade procedure described above. - run `borg compact --cleanup-commits` to clean up a ton of 17 bytes long files in your repo caused by a borg 1.1 bug - run `borg check` again (now with borg 1.2.x) and check if there is anything special. - run `borg info` (with borg 1.2.x) to build the local pre12-meta cache (can take significant time, but after that it will be fast) - for more details see below. - check the compatibility notes (see below) and adapt your scripts, if needed. - if you run into any issues, please check the github issue tracker before posting new issues there or elsewhere. If you follow this procedure, you can help avoiding that we get a lot of "borg 1.2" issue reports that are not really 1.2 issues, but existed before and maybe just were not noticed. Compatibility notes: - matching of path patterns has been aligned with borg storing relative paths. Borg archives file paths without leading slashes. Previously, include/exclude patterns could contain leading slashes. You should check your patterns and remove leading slashes. - dropped support / testing for older Pythons, minimum requirement is 3.8. In case your OS does not provide Python >= 3.8, consider using our binary, which does not need an external Python interpreter. Or continue using borg 1.1.x, which is still supported. - freeing repository space only happens when "borg compact" is invoked. - mount: the default for --numeric-ids is False now (same as borg extract) - borg create --noatime is deprecated. Not storing atime is the default behaviour now (use --atime if you want to store the atime). - --prefix is deprecated, use -a / --glob-archives, see #6806 - list: corrected mix-up of "isomtime" and "mtime" formats. Previously, "isomtime" was the default but produced a verbose human format, while "mtime" produced a ISO-8601-like format. The behaviours have been swapped (so "mtime" is human, "isomtime" is ISO-like), and the default is now "mtime". "isomtime" is now a real ISO-8601 format ("T" between date and time, not a space). - create/recreate --list: file status for all files used to get announced *AFTER* the file (with borg < 1.2). Now, file status is announced *BEFORE* the file contents are processed. If the file status changes later (e.g. due to an error or a content change), the updated/final file status will be printed again. - removed deprecated-since-long stuff (deprecated since): - command "borg change-passphrase" (2017-02), use "borg key ..." - option "--keep-tag-files" (2017-01), use "--keep-exclude-tags" - option "--list-format" (2017-10), use "--format" - option "--ignore-inode" (2017-09), use "--files-cache" w/o "inode" - option "--no-files-cache" (2017-09), use "--files-cache=disabled" - removed BORG_HOSTNAME_IS_UNIQUE env var. to use borg you must implement one of these 2 scenarios: - 1) the combination of FQDN and result of uuid.getnode() must be unique and stable (this should be the case for almost everybody, except when having duplicate FQDN *and* MAC address or all-zero MAC address) - 2) if you are aware that 1) is not the case for you, you must set BORG_HOST_ID env var to something unique. - exit with 128 + signal number, #5161. if you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128. .. _changelog_1x: Change Log 1.x ============== Version 1.3.0a1 (2022-04-15) ---------------------------- Please note: This is an alpha release, only for testing - do not use this with production repos. New features: - init: new --encryption=(repokey|keyfile)-[blake2-](aes-ocb|chacha20-poly1305) - New, better, faster crypto (see encryption-aead diagram in the docs), #6463. - New AEAD cipher suites: AES-OCB and CHACHA20-POLY1305. - Session keys are derived via HKDF from random session id and master key. - Nonces/MessageIVs are counters starting from 0 for each session. - AAD: chunk id, key type, messageIV, sessionID are now authenticated also. - Solves the potential AES-CTR mode counter management issues of the legacy crypto. - init: --key-algorithm=argon2 (new default KDF, older pbkdf2 also still available) borg key change-passphrase / change-location keeps the key algorithm unchanged. - key change-algorithm: to upgrade existing keys to argon2 or downgrade to pbkdf2. We recommend you to upgrade unless you have to keep the key compatible with older versions of borg. - key change-location: usable for repokey <-> keyfile location change - benchmark cpu: display benchmarks of cpu bound stuff - export-tar: new --tar-format=PAX (default: GNU) - import-tar/export-tar: can use PAX format for ctime and atime support - import-tar/export-tar: --tar-format=BORG: roundtrip ALL item metadata, #5830 - repository: create and use version 2 repos only for now - repository: implement PUT2: header crc32, overall xxh64, #1704 Other changes: - require python >= 3.9, #6315 - simplify libs setup, #6482 - unbundle most bundled 3rd party code, use libs, #6316 - use libdeflate.crc32 (Linux and all others) or zlib.crc32 (macOS) - repository: code cleanups / simplifications - internal crypto api: speedups / cleanups / refactorings / modernisation - remove "borg upgrade" support for "attic backup" repos - remove PassphraseKey code and borg key migrate-to-repokey command - OpenBSD: build borg with OpenSSL (not: LibreSSL), #6474 - remove support for LibreSSL, #6474 - remove support for OpenSSL < 1.1.1 Version 1.2.7 (2023-12-02) -------------------------- For upgrade and compatibility hints, please also read the section "Upgrade Notes" above. Fixes: - docs: CVE-2023-36811 upgrade steps: consider checkpoint archives, #7802 - check/compact: fix spurious reappearance of orphan chunks since borg 1.2, #6687 - this consists of 2 fixes: - for existing chunks: check --repair: recreate shadow index, #7897 #6687 - for newly created chunks: update shadow index when doing a double-put, #7896 #5661 If you have experienced issue #6687, you may want to run borg check --repair after upgrading to borg 1.2.7 to recreate the shadow index and get rid of the issue for existing chunks. - LockRoster.modify: no KeyError if element was already gone, #7937 - create --X-from-command: run subcommands with a clean environment, #7916 - list --sort-by: support "archive" as alias of "name", #7873 - fix rc and msg if arg parsing throws an exception, #7885 Other changes: - support and test on Python 3.12 - include unistd.h in _chunker.c (fix for Python 3.13) - allow msgpack 1.0.6 and 1.0.7 - TAM issues: show tracebacks, improve borg check logging, #7797 - replace "datetime.utcfromtimestamp" with custom helper to avoid deprecation warnings when using Python 3.12 - vagrant: - use generic/debian9 box, fixes #7579 - add VM with debian bookworm / test on OpenSSL 3.0.x. - docs: - not only attack/unsafe, can also be a fs issue, #7853 - point to CVE-2023-36811 upgrade steps from borg 1.1 to 1.2 upgrade steps, #7899 - upgrade steps needed for all kinds of repos (including "none" encryption mode), #7813 - upgrade steps: talk about consequences of borg check, #7816 - upgrade steps: remove period that could be interpreted as part of the command - automated-local.rst: use GPT UUID for consistent udev rule - create disk/partition sector backup by disk serial number, #7934 - update macOS hint about full disk access - clarify borg prune -a option description, #7871 - readthedocs: also build offline docs (HTMLzip), #7835 - frontends: add "check.rebuild_refcounts" message Version 1.2.6 (2023-08-31) -------------------------- Fixes: - The upgrade procedure docs as published with borg 1.2.5 did not work, if the repository had archives resulting from a borg rename or borg recreate operation. The updated docs now use BORG_WORKAROUNDS=ignore_invalid_archive_tam at some places to avoid that issue, #7791. See: fix pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), details and necessary upgrade procedure described above. Other changes: - updated 1.2.5 changelog entry: 1.2.5 already has the fix for rename/recreate. - remove cython restrictions. recommended is to build with cython 0.29.latest, because borg 1.2.x uses this since years and it is very stable. you can also try to build with cython 3.0.x, there is a good chance that it works. as a 3rd option, we also bundle the `*.c` files cython outputs in the release pypi package, so you can also just use these and not need cython at all. Version 1.2.5 (2023-08-30) -------------------------- Fixes: - Security: fix pre-1.2.5 archives spoofing vulnerability (CVE-2023-36811), see details and necessary upgrade procedure described above. - rename/recreate: correctly update resulting archive's TAM, see #7791 - create: do not try to read parent dir of recursion root, #7746 - extract: fix false warning about pattern never matching, #4110 - diff: remove surrogates before output, #7535 - compact: clear empty directories at end of compact process, #6823 - create --files-cache=size: fix crash, #7658 - keyfiles: improve key sanity check, #7561 - only warn about "invalid" chunker params, #7590 - ProgressIndicatorPercent: fix space computation for wide chars, #3027 - improve argparse validator error messages New features: - mount: make up volname if not given (macOS), #7690. macFUSE supports a volname mount option to give what finder displays on the desktop / in the directory view. if the user did not specify it, we make something up, because otherwise it would be "macFUSE Volume 0 (Python)" and hide the mountpoint directory name. - BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without key, #7700 Other changes: - add `utcnow()` helper function to avoid deprecated `datetime.utcnow()` - stay on latest Cython 0.29 (0.29.36) for borg 1.2.x (do not use Cython 3.0 yet) - docs: - move upgrade notes to own section, see #7546 - mount -olocal: how to show mount in finder's sidebar, #5321 - list: fix --pattern examples, #7611 - improve patterns help - incl./excl. options, path-from-stdin exclusiveness - obfuscation docs: markup fix, note about MAX_DATA_SIZE - --one-file-system: add macOS apfs notes, #4876 - improve --one-file-system help string, #5618 - rewrite borg check docs - improve the docs for --keep-within, #7687 - fix borg init command in environment.rst.inc - 1.1.x upgrade notes: more precise borg upgrade instructions, #3396 - tests: - fix repo reopen - avoid long ids in pytest output - check buzhash chunksize distribution, see #7586 Version 1.2.4 (2023-03-24) -------------------------- New features: - import-tar: add --ignore-zeros to process concatenated tars, #7432. - debug id-hash: computes file/chunk content id-hash, #7406 - diff: --content-only does not show mode/ctime/mtime changes, #7248 - diff: JSON strings in diff output are now sorted alphabetically Bug fixes: - xattrs: fix namespace processing on FreeBSD, #6997 - diff: fix path related bug seen when addressing deferred items. - debug get-obj/put-obj: always give chunkid as cli param, see #7290 (this is an incompatible change, see also borg debug id-hash) - extract: fix mtime when ResourceFork xattr is set (macOS specific), #7234 - recreate: without --chunker-params, do not re-chunk, #7337 - recreate: when --target is given, do not detect "nothing to do". use case: borg recreate -a src --target dst can be used to make a copy of an archive inside the same repository, #7254. - set .hardlink_master for ALL hardlinkable items, #7175 - locking: fix host, pid, tid order. tid (thread id) must be parsed as hex from lock file name. - update development.lock.txt, including a setuptools security fix, #7227 Other changes: - requirements: allow msgpack 1.0.5 also - upgrade Cython to 0.29.33 - hashindex minor fixes, refactor, tweaks, tests - use os.replace not os.rename - remove BORG_LIBB2_PREFIX (not used any more) - docs: - BORG_KEY_FILE: clarify docs, #7444 - update FAQ about locale/unicode issues, #6999 - improve mount options rendering, #7359 - make timestamps in manual pages reproducible - installation: update Fedora in distribution list, #7357 - tests: - fix test_size_on_disk_accurate for large st_blksize, #7250 - add same_ts_ns function and use it for relaxed timestamp comparisons - "auto" compressor tests: don't assume a specific size, do not assume zlib is better than lz4, #7363 - add test for extracted directory mtime - vagrant: - upgrade local freebsd 12.1 box -> generic/freebsd13 box (13.1) - use pythons > 3.8 which work on freebsd 13.1 - pyenv: also install python 3.11.1 for testing - pyenv: use python 3.10.1, 3.10.0 build is broken on freebsd Version 1.2.3 (2022-12-24) -------------------------- Fixes: - create: fix --list --dry-run output for directories, #7209 - diff/recreate: normalize chunker params before comparing them, #7079 - check: fix uninitialised variable if repo is completely empty, #7034 - xattrs: improve error handling, #6988 - fix args.paths related argparsing, #6994 - archive.save(): always use metadata from stats (e.g. nfiles, size, ...), #7072 - tar_filter: recognize .tar.zst as zstd, #7093 - get_chunker: fix missing sparse=False argument, #7056 - file_integrity.py: make sure file_fd is always closed on exit - repository: cleanup(): close segment before unlinking - repository: use os.replace instead of os.rename Other changes: - remove python < 3.7 compatibility code - do not use version_tuple placeholder in setuptools_scm template - CI: fix tox4 passenv issue, #7199 - vagrant: update to python 3.9.16, use the openbsd 7.1 box - misc. test suite and docs fixes / improvements - remove deprecated --prefix from docs, #7109 - Windows: use MSYS2 for Github CI, remove Appveyor CI Version 1.2.2 (2022-08-20) -------------------------- New features: - prune/delete --checkpoint-interval=1800 and ctrl-c/SIGINT support, #6284 Fixes: - SaveFile: use a custom mkstemp with mode support, #6933, #6400, #6786. This fixes umask/mode/ACL issues (and also "chmod not supported" exceptions seen in 1.2.1) of files updated using SaveFile, e.g. the repo config. - hashindex_compact: fix eval order (check idx before use), #5899 - create --paths-from-(stdin|command): normalize paths, #6778 - secure_erase: avoid collateral damage, #6768. If a hardlink copy of a repo was made and a new repo config shall be saved, do NOT fill in random garbage before deleting the previous repo config, because that would damage the hardlink copy. - list: fix {flags:} formatting, #6081 - check: try harder to create the key, #5719 - misc commands: ctrl-c must not kill other subprocesses, #6912 - borg create with a remote repo via ssh - borg create --content-from-command - borg create --paths-from-command - (de)compression filter process of import-tar / export-tar Other changes: - deprecate --prefix, use -a / --glob-archives, see #6806 - make setuptools happy ("package would be ignored"), #6874 - fix pyproject.toml to create a fixed _version.py file, compatible with both old and new setuptools_scm version, #6875 - automate asciinema screencasts - CI: test on macOS 12 without fuse / fuse tests (too troublesome on github CI due to kernel extensions needed by macFUSE) - tests: fix test_obfuscate byte accounting - repository: add debug logging for issue #6687 - _chunker.c: fix warnings on macOS - requirements.lock.txt: use the latest cython 0.29.32 - docs: - add info on man page installation, #6894 - update archive_progress json description about "finished", #6570 - json progress_percent: some values are optional, #4074 - FAQ: full quota / full disk, #5960 - correct shell syntax for installation using git Version 1.2.1 (2022-06-06) -------------------------- Fixes: - create: skip with warning if opening the parent dir of recursion root fails, #6374 - create: fix crash. metadata stream can produce all-zero chunks, #6587 - fix crash when computing stats, escape % chars in archive name, #6500 - fix transaction rollback: use files cache filename as found in txn.active/, #6353 - import-tar: kill filter process in case of borg exceptions, #6401 #6681 - import-tar: fix mtime type bug - ensure_dir: respect umask for created directory modes, #6400 - SaveFile: respect umask for final file mode, #6400 - check archive: improve error handling for corrupt archive metadata block, make robust_iterator more robust, #4777 - pre12-meta cache: do not use the cache if want_unique is True, #6612 - fix scp-style repo url parsing for ip v6 address, #6526 - mount -o versions: give clear error msg instead of crashing. it does not make sense to request versions view if you only look at 1 archive, but the code shall not crash in that case as it did, but give a clear error msg. - show_progress: add finished=true/false to archive_progress json, #6570 - delete/prune: fix --iec mode output (decimal vs. binary units), #6606 - info: fix authenticated mode repo to show "Encrypted: No", #6462 - diff: support presence change for blkdev, chrdev and fifo items, #6615 New features: - delete: add repository id and location to prompt, #6453 - borg debug dump-repo-objs --ghost: new --segment=S --offset=O options Other changes: - support python 3.11 - allow msgpack 1.0.4, #6716 - load_key: no key is same as empty key, #6441 - give a more helpful error msg for unsupported key formats, #6561 - better error msg for defect or unsupported repo configs, #6566 - docs: - document borg 1.2 pattern matching behavior change, #6407 Make clear that absolute paths always go into the matcher as if they are relative (without leading slash). Adapt all examples accordingly. - authentication primitives: improved security and performance infos - mention BORG_FILES_CACHE_SUFFIX as alternative to BORG_FILES_CACHE_TTL, #5602 - FAQ: add a hint about --debug-topic=files_cache - improve borg check --max-duration description - fix values of TAG bytes, #6515 - borg compact --cleanup-commits also runs a normal compaction, #6324 - virtualization speed tips - recommend umask for passphrase file perms - borg 1.2 is security supported - update link to ubuntu packages, #6485 - use --numeric-ids in pull mode docs - remove blake2 docs, blake2 code not bundled any more, #6371 - clarify on-disk order and size of segment file log entry fields, #6357 - docs building: do not transform --/--- to unicode dashes - tests: - check that borg does not require pytest for normal usage, fixes #6563 - fix OpenBSD symlink mode test failure, #2055 - vagrant: - darwin64: remove fakeroot, #6314 - update development.lock.txt - use pyinstaller 4.10 and python 3.9.13 for binary build - upgrade VMCPUS and xdistn from 4 to 16, maybe this speeds up the tests - crypto: - use hmac.compare_digest instead of ==, #6470 - hmac_sha256: replace own cython wrapper code by hmac.digest python stdlib (since py38) - hmac and blake2b minor optimizations and cleanups - removed some unused crypto related code, #6472 - avoid losing the key (potential use-after-free). this never could happen in 1.2 due to the way we use the code. The issue was discovered in master after other changes, so we also "fixed" it here before it bites us. - setup / build: - add pyproject.toml, fix sys.path, #6466 - setuptools_scm: also require it via pyproject.toml - allow extra compiler flags for every extension build - fix misc. C / Cython compiler warnings, deprecation warnings - fix zstd.h include for bundled zstd, #6369 - source using python 3.8 features: ``pyupgrade --py38-plus ./**/*.py`` Version 1.2.0 (2022-02-22 22:02:22 :-) -------------------------------------- Please note: This is the first borg 1.2 release, so be careful and read the notes below. Upgrade notes: Strictly taken, nothing special is required for upgrading to 1.2, but some things can be recommended: - do you already want to upgrade? 1.1.x also will get fixes for a while. - be careful, first upgrade your less critical / smaller repos. - first upgrade to a recent 1.1.x release - especially if you run some older 1.1.* or even 1.0.* borg release. - using that, run at least one `borg create` (your normal backup), `prune` and especially a `check` to see everything is in a good state. - check the output of `borg check` - if there is anything special, consider a `borg check --repair` followed by another `borg check`. - if everything is fine so far (borg check reports no issues), you can consider upgrading to 1.2.0. if not, please first fix any already existing issue. - if you want to play safer, first **create a backup of your borg repository**. - upgrade to latest borg 1.2.x release (you could use the fat binary from github releases page) - run `borg compact --cleanup-commits` to clean up a ton of 17 bytes long files in your repo caused by a borg 1.1 bug - run `borg check` again (now with borg 1.2.x) and check if there is anything special. - run `borg info` (with borg 1.2.x) to build the local pre12-meta cache (can take significant time, but after that it will be fast) - for more details see below. - check the compatibility notes (see below) and adapt your scripts, if needed. - if you run into any issues, please check the github issue tracker before posting new issues there or elsewhere. If you follow this procedure, you can help avoiding that we get a lot of "borg 1.2" issue reports that are not really 1.2 issues, but existed before and maybe just were not noticed. Compatibility notes: - matching of path patterns has been aligned with borg storing relative paths. Borg archives file paths without leading slashes. Previously, include/exclude patterns could contain leading slashes. You should check your patterns and remove leading slashes. - dropped support / testing for older Pythons, minimum requirement is 3.8. In case your OS does not provide Python >= 3.8, consider using our binary, which does not need an external Python interpreter. Or continue using borg 1.1.x, which is still supported. - freeing repository space only happens when "borg compact" is invoked. - mount: the default for --numeric-ids is False now (same as borg extract) - borg create --noatime is deprecated. Not storing atime is the default behaviour now (use --atime if you want to store the atime). - list: corrected mix-up of "isomtime" and "mtime" formats. Previously, "isomtime" was the default but produced a verbose human format, while "mtime" produced a ISO-8601-like format. The behaviours have been swapped (so "mtime" is human, "isomtime" is ISO-like), and the default is now "mtime". "isomtime" is now a real ISO-8601 format ("T" between date and time, not a space). - create/recreate --list: file status for all files used to get announced *AFTER* the file (with borg < 1.2). Now, file status is announced *BEFORE* the file contents are processed. If the file status changes later (e.g. due to an error or a content change), the updated/final file status will be printed again. - removed deprecated-since-long stuff (deprecated since): - command "borg change-passphrase" (2017-02), use "borg key ..." - option "--keep-tag-files" (2017-01), use "--keep-exclude-tags" - option "--list-format" (2017-10), use "--format" - option "--ignore-inode" (2017-09), use "--files-cache" w/o "inode" - option "--no-files-cache" (2017-09), use "--files-cache=disabled" - removed BORG_HOSTNAME_IS_UNIQUE env var. to use borg you must implement one of these 2 scenarios: - 1) the combination of FQDN and result of uuid.getnode() must be unique and stable (this should be the case for almost everybody, except when having duplicate FQDN *and* MAC address or all-zero MAC address) - 2) if you are aware that 1) is not the case for you, you must set BORG_HOST_ID env var to something unique. - exit with 128 + signal number, #5161. if you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128. Fixes: - diff: reduce memory consumption, fix is_hardlink_master, #6295 - compact: fix / improve freeable / freed space log output - derive really freed space from quota use before/after, #5679 - do not say "freeable", but "maybe freeable" (based on hint, unsure) - fix race conditions in internal SaveFile function, #6306 #6028 - implement internal safe_unlink (was: truncate_and_unlink) function more safely: usually it does not truncate any more, only under "disk full" circumstances and only if there is only one hardlink. see: https://github.com/borgbackup/borg/discussions/6286 Other changes: - info: use a pre12-meta cache to accelerate stats for borg < 1.2 archives. the first time borg info is invoked on a borg 1.1 repo, it can take a rather long time computing and caching some stats values for 1.1 archives, which borg 1.2 archives have in their archive metadata structure. be patient, esp. if you have lots of old archives. following invocations are much faster due to the cache. related change: add archive name to calc_stats progress display. - docs: - add borg 1.2 upgrade notes, #6217 - link to borg placeholders and borg patterns help - init: explain the encryption modes better - clarify usage of patternfile roots - put import-tar docs into same file as export-tar docs - explain the difference between a path that ends with or without a slash, #6297 Version 1.2.0rc1 (2022-02-05) ----------------------------- Fixes: - repo::archive location placeholder expansion fixes, #5826, #5998 - repository: fix intermediate commits, shall be at end of current segment - delete: don't commit if nothing was deleted, avoid cache sync, #6060 - argument parsing: accept some options only once, #6026 - disallow overwriting of existing keyfiles on init, #6036 - if ensure_dir() fails, give more informative error message, #5952 New features: - delete --force: do not ask when deleting a repo, #5941 Other changes: - requirements: exclude broken or incompatible-with-pyinstaller setuptools - add a requirements.d/development.lock.txt and use it for vagrant - tests: - added nonce-related tests - refactor: remove assert_true - vagrant: macos box tuning, netbsd box fixes, #5370, #5922 - docs: - update install docs / requirements docs, #6180 - borg mount / FUSE "versions" view is not experimental any more - --pattern* is not experimental any more, #6134 - impact of deleting path/to/repo/nonce, #5858 - key export: add examples, #6204 - ~/.config/borg/keys is not used for repokey keys, #6107 - excluded parent dir's metadata can't restore Version 1.2.0b4 (2022-01-23) ---------------------------- Fixes: - create: fix passing device nodes and symlinks to --paths-from-stdin, #6009 - create --dry-run: fix display of kept tagfile, #5834 - check --repair: fix missing parameter in "did not consistently fail" msg, #5822 - fix hardlinkable file type check, #6037 - list: remove placeholders for shake_* hashes, #6082 - prune: handle case of calling prune_split when there are no archives, #6015 - benchmark crud: make sure cleanup of borg-test-data files/dir happens, #5630 - do not show archive name in repository-related error msgs, #6014 - prettier error msg (no stacktrace) if exclude file is missing, #5734 - do not require BORG_CONFIG_DIR if BORG_{SECURITY,KEYS}_DIR are set, #5979 - fix pyinstaller detection for dir-mode, #5897 - atomically create the CACHE_TAG file, #6028 - deal with the SaveFile/SyncFile race, docs, see #6056 708a5853 - avoid expanding path into LHS of formatting operation + tests, #6064 #6063 - repository: quota / compactable computation fixes - info: emit repo info even if repo has 0 archives + test, #6120 New features: - check --repair: significantly speed up search for next valid object in segment, #6022 - check: add progress indicator for archive check, #5809 - create: add retry_erofs workaround for O_NOATIME issue on volume shadow copies in WSL1, #6024 - create: allow --files-cache=size (this is potentially dangerous, use on your own risk), #5686 - import-tar: implement import-tar to complement export-tar, #2233 - implement BORG_SELFTEST env variable (can be carefully used to speedup borg hosting), #5871 - key export: print key if path is '-' or not given, #6092 - list --format: Add command_line to format keys Other changes: - pypi metadata: alpha -> beta - require python 3.8+, #5975 - use pyinstaller 4.7 - allow msgpack 1.0.3 - upgrade to bundled xxhash to 0.8.1 - import-tar / export-tar: tar file related changes: - check for short tarfile extensions - add .lz4 and .zstd - fix docs about extensions and decompression commands - add github codeql analysis, #6148 - vagrant: - box updates / add new boxes / remove outdated and broken boxes - use Python 3.9.10 (incl. binary builds) and 3.10.0 - fix pyenv initialisation, #5798 - fix vagrant scp on macOS, #5921 - use macfuse instead of osxfuse - shell completions: - update shell completions to 1.1.17, #5923 - remove BORG_LIBC completion, since 9914968 borg no longer uses find_library(). - docs: - fixed readme.rst irc webchat link (we use libera chat now, not freenode) - fix exceptions thrown by `setup.py build_man` - check --repair: recommend checking hw before check --repair, #5855 - check --verify-data: clarify and document conflict with --repository-only, #5808 - serve: improve ssh forced commands docs, #6083 - list: improve docs for `borg list` --format, #6061 - list: remove --list-format from borg list - FAQ: fix manifest-timestamp path (inside security dir) - fix the broken link to .nix file - document behavior for filesystems with inconsistent inodes, #5770 - clarify user_id vs uid for fuse, #5723 - clarify pattern usage with commands, #5176 - clarify pp vs. pf pattern type, #5300 - update referenced freebsd/macOS versions used for binary build, #5942 - pull mode: add some warnings, #5827 - clarify "you will need key and passphrase" borg init warning, #4622 - add missing leading slashes in help patterns, #5857 - add info on renaming repositories, #5240 - check: add notice about defective hardware, #5753 - mention tar --compare (compare archive to fs files), #5880 - add note about grandfather-father-son backup retention policy / rotation scheme, #6006 - permissions note rewritten to make it less confusing - create github security policy - remove leftovers of BORG_HOSTNAME_IS_UNIQUE - excluded parent dir's metadata can't restore. (#6062) - if parent dir is not extracted, we do not have its metadata - clarify who starts the remote agent Version 1.2.0b3 (2021-05-12) ---------------------------- Fixes: - create: fix --progress --log-json, #4360#issuecomment-774580052 - do not load files cache for commands not using it, #5673 - fix repeated cache tag file writing bug New features: - create/recreate: print preliminary file status early, #5417 - create/extract: add --noxattrs and --noacls options, #3955 - create: verbose files cache logging via --debug-topic=files_cache, #5659 - mount: implement --numeric-ids (default: False!), #2377 - diff: add --json-lines option - info / create --stats: add --iec option to print sizes in powers of 1024. Other changes: - create: add --upload-(ratelimit|buffer), deprecate --remote-* options, #5611 - create/extract/mount: add --numeric-ids, deprecate --numeric-owner option, #5724 - config: accept non-int value for max_segment_size / storage_quota - use PyInstaller v4.3, #5671 - vagrant: use Python 3.9.5 to build binaries - tox.ini: modernize and enable execution without preinstalling deps - cleanup code style checks - get rid of distutils, use setuptools+packaging - github CI: test on Python 3.10-dev - check: missing / healed chunks: always tell chunk ID, #5704 - docs: - remove bad /var/cache exclusion in example commands, #5625 - misc. fixes and improvements, esp. for macOS - add unsafe workaround to use an old repo copy, #5722 Version 1.2.0b2 (2021-02-06) ---------------------------- Fixes: - create: do not recurse into duplicate roots, #5603 - create: only print stats if not ctrl-c'ed, fixes traceback, #5668 - extract: improve exception handling when setting xattrs, #5092. emit a warning message giving the path, xattr key and error message. continue trying to restore other xattrs and bsdflags of the same file after an exception with xattr-setting happened. - export-tar: fix memory leak with ssh: remote repository, #5568. fix potential memory leak with ssh: remote repository with partial extraction. - remove empty shadowed_segments lists, #5275 - fix bad default: manifest.archives.list(consider_checkpoints=False), fixes tracebacks / KeyErros for missing objects in ChunkIndex, #5668 New features: - create: improve sparse file support - create --sparse (detect sparse file holes) and file map support, only for the "fixed" chunker, #14 - detect all-zero chunks in read data in "buzhash" and "fixed" chunkers - cached_hash: use a small LRU cache to accelerate all-zero chunks hashing - use cached_hash also to generate all-zero replacement chunks - create --remote-buffer, add a upload buffer for remote repos, #5574 - prune: keep oldest archive when retention target not met Other changes: - use blake2 from python 3.6+ hashlib (this removes the requirement for libb2 and the bundled blake2 code) - also accept msgpack up to 1.0.2. exclude 1.0.1 though, which had some issues (not sure they affect borg). - create: add repository location to --stats output, #5491 - check: debug log the segment filename - delete: add a --list switch to borg delete, #5116 - borg debug dump-hints - implemented to e.g. to look at shadow_index - Tab completion support for additional archives for 'borg delete' - refactor: have one borg.constants.zero all-zero bytes object - refactor shadow_index updating repo.put/delete, #5661, #5636. - docs: - add another case of attempted hardlink usage - fix description of borg upgrade hardlink usage, #5518 - use HTTPS everywhere - add examples for --paths-from-stdin, --paths-from-command, --paths-separator, #5644 - fix typos/grammar - update docs for dev environment installation instructions - recommend running tests only on installed versions for setup - add badge with current status of package - vagrant: - use brew install --cask ..., #5557 - use Python 3.9.1 and PyInstaller 4.1 to build the borg binary Version 1.2.0b1 (2020-12-06) ---------------------------- Fixes: - BORG_CACHE_DIR crashing borg if empty, atomic handling of recursive directory creation, #5216 - fix --dry-run and --stats coexistence, #5415 - allow EIO with warning when trying to hardlink, #4336 - export-tar: set tar format to GNU_FORMAT explicitly, #5274 - use --timestamp for {utcnow} and {now} if given, #5189 - make timestamp helper timezone-aware New features: - create: implement --paths-from-stdin and --paths-from-command, see #5492. These switches read paths to archive from stdin. Delimiter can specified by --paths-delimiter=DELIM. Paths read will be added honoring every option but exclusion options and --one-file-system. borg won't recurse into directories. - 'obfuscate' pseudo compressor obfuscates compressed chunk size in repo - add pyfuse3 (successor of llfuse) as an alternative lowlevel fuse implementation to llfuse (deprecated), #5407. FUSE implementation can be switched via env var BORG_FUSE_IMPL. - allow appending to the files cache filename with BORG_FILES_CACHE_SUFFIX - create: implement --stdin-mode, --stdin-user and --stdin-group, #5333 Other changes: - split recursive directory walking/processing into directory walking and item processing. - fix warning by importing setuptools before distutils. - debug info: include infos about FUSE implementation, #5546 - testing: - add a test for the hashindex corruption bug, #5531 #4829 - move away from travis-ci, use github actions, #5528 #5467 - test both on fuse2 and fuse3 - upload coverage reports to codecov - fix spurious failure in test_cache_files, #5438 - add tests for Location.with_timestamp - tox: add a non-fuse env to the envlist - vagrant: - use python 3.7.latest and pyinstaller 4.0 for binary creation - pyinstaller: compute basepath from spec file location - vagrant: updates/fixes for archlinux box, #5543 - docs: - "filename with spaces" example added to exclude file, #5236 - add a hint about sleeping computer, #5301 - how to adjust macOS >= Catalina security settings, #5303 - process/policy for adding new compression algorithms - updated docs about hacked backup client, #5480 - improve ansible deployment docs, make it more generic - how to approach borg speed issues, give speed example, #5371 - fix mathematical inaccuracy about chunk size, #5336 - add example for excluding content using --pattern cli option - clarify borg create's '--one-file-system' option, #4009 - improve docs/FAQ about append-only remote repos, #5497 - fix reST markup issues, labels - add infos about contributor retirement status Version 1.2.0a9 (2020-10-05) ---------------------------- Fixes: - fix memory leak related to preloading, #5202 - check --repair: fix potential data loss, #5325 - persist shadow_index in between borg runs, #4830 - fix hardlinked CACHEDIR.TAG processing, #4911 - --read-special: .part files also should be regular files, #5217 - allow server side enforcing of umask, --umask is for the local borg process only (see docs), #4947 - exit with 128 + signal number, #5161 - borg config --list does not show last_segment_checked, #5159 - locking: - fix ExclusiveLock race condition bug, #4923 - fix race condition in lock migration, #4953 - fix locking on openindiana, #5271 New features: - --content-from-command: create archive using stdout of given command, #5174 - allow key-import + BORG_KEY_FILE to create key files - build directory-based binary for macOS to avoid Gatekeeper delays Other changes: - upgrade bundled zstd to 1.4.5 - upgrade bundled xxhash to 0.8.0, #5362 - if self test fails, also point to OS and hardware, #5334 - misc. shell completions fixes/updates, rewrite zsh completion - prettier error message when archive gets too big, #5307 - stop relying on `false` exiting with status code 1 - rephrase some warnings, #5164 - parseformat: unnecessary calls removed, #5169 - testing: - enable Python3.9 env for test suite and VMs, #5373 - drop python 3.5, #5344 - misc. vagrant fixes/updates - misc. testing fixes, #5196 - docs: - add ssh-agent pull backup method to doc, #5288 - mention double --force in prune docs - update Homebrew install instructions, #5185 - better description of how cache and rebuilds of it work and how the workaround applies to that - point to borg create --list item flags in recreate usage, #5165 - add a note to create from stdin regarding files cache, #5180 - add security faq explaining AES-CTR crypto issues, #5254 - clarify --exclude-if-present in recreate, #5193 - add socat pull mode, #5150, #900 - move content of resources doc page to community project, #2088 - explain hash collision, #4884 - clarify --recompress option, #5154 Version 1.2.0a8 (2020-04-22) ---------------------------- Fixes: - fixed potential index corruption / data loss issue due to bug in hashindex_set, #4829. Please read and follow the more detailed notes close to the top of this document. - fix crash when upgrading erroneous hints file, #4922 - commit-time free space calc: ignore bad compact map entries, #4796 - info: if the archive doesn't exist, print a pretty message, #4793 - --prefix / -P: fix processing, avoid argparse issue, #4769 - ignore EACCES (errno 13) when hardlinking, #4730 - add a try catch when formatting the info string, #4818 - check: do not stumble over invalid item key, #4845 - update prevalence of env vars to set config and cache paths - mount: fix FUSE low linear read speed on large files, #5032 - extract: fix confusing output of borg extract --list --strip-components, #4934 - recreate: support --timestamp option, #4745 - fix ProgressIndicator msgids (JSON output), #4935 - fuse: set f_namemax in statfs result, #2684 - accept absolute paths on windows - pyinstaller: work around issue with setuptools > 44 New features: - chunker speedup (plus regression test) - added --consider-checkpoints and related test, #4788 - added --noflags option, deprecate --nobsdflags option, #4489 - compact: add --threshold option, #4674 - mount: add birthtime to FUSE entries - support platforms with no os.link, #4901 - if we don't have os.link, we just extract another copy instead of making a hardlink. - move sync_file_range to its own extension for better platform compatibility. - new --bypass-lock option to bypass locking, e.g. for read-only repos - accept absolute paths by removing leading slashes in patterns of all sorts but re: style, #4029 - delete: new --keep-security-info option Other changes: - support msgpack 0.6.2 and 1.0.0, #5065 - upgrade bundled zstd to 1.4.4 - upgrade bundled lz4 to 1.9.2 - upgrade xxhash to 0.7.3 - require recent enough llfuse for birthtime support, #5064 - only store compressed data if the result actually is smaller, #4516 - check: improve error output for matching index size, see #4829 - ignore --stats when given with --dry-run, but continue, #4373 - replaced usage of os.statvfs with shutil.disk_usage (better cross-platform support). - fuse: remove unneeded version check and compat code, micro opts - docs: - improve description of path variables - document how to delete data completely, #2929 - add FAQ about Borg config dir, #4941 - add docs about errors not printed as JSON, #4073 - update usage_general.rst.inc - added "Will move with BORG_CONFIG_DIR variable unless specified." to BORG_SECURITY_DIR info. - put BORG_SECURITY_DIR immediately below BORG_CONFIG_DIR (and moved BORG_CACHE_DIR up before them). - add paragraph regarding cache security assumptions, #4900 - tell about borg cache security precautions - add FAQ describing difference between a local repo vs. repo on a server. - document how to test exclusion patterns without performing an actual backup - create: tell that "Calculating size" time and space needs are caused by --progress - fix/improve documentation for @api decorator, #4674 - add a pull backup / push restore how-to, #1552 - fix man pages creation, #4752 - more general FAQ for backup and retain original paths, #4532 - explain difference between --exclude and --pattern, #4118 - add FAQ for preventing SSH timeout in extract, #3866 - improve password FAQ (decrease pw length, add -w 0 option to base64 to prevent line wrap), #4591 - add note about patterns and stored paths, #4160 - add upgrade of tools to pip installation how-to, #5090 - document one cause of orphaned chunks in check command, #2295 - clean up the whole check usage paragraph - FAQ: linked recommended restrictions to ssh public keys on borg servers, #4946 - fixed "doc downplays severity of Nonce reuse issue", #4883 - borg repo restore instructions needed, #3428 - new FAQ: A repo is corrupt and must be replaced with an older repo. - clarify borg init's encryption modes - native windows port: - update README_WINDOWS.rst - updated pyinstaller spec file to support windows builds - testing / CI: - improved travis config / install script, improved macOS builds - allow osx builds to fail, #4955 - Windows 10 build on Appveyor CI - vagrant: - upgrade pyinstaller to v3.5 + patch - use py369 for binary build, add py380 for tests - fix issue in stretch VM hanging at grub installation - add a debian buster and a ubuntu focal VM - update darwin box to 10.12 - upgrade FreeBSD box to 12.1 - fix debianoid virtualenv packages - use pyenv in freebsd64 VM - remove the flake8 test - darwin: avoid error if pkg is already installed - debianoid: don't interactively ask questions Version 1.2.0a7 (2019-09-07) ---------------------------- Fixes: - slave hardlinks extraction issue, see #4350 - extract: fix KeyError for "partial" extraction, #4607 - preload chunks for hardlink slaves w/o preloaded master, #4350 - fix preloading for old remote servers, #4652 - fix partial extract for hardlinked contentless file types, #4725 - Repository.open: use stat() to check for repo dir, #4695 - Repository.check_can_create_repository: use stat() to check, ~ #4695. - SecurityManager.known(): check all files, #4614 - after double-force delete, warn about necessary repair, #4704 - cope with ANY error when importing pytest into borg.testsuite, #4652 - fix invalid archive error message - setup.py: fix detection of missing Cython - filter out selinux xattrs, #4574 - location arg - should it be optional? #4541 - enable placeholder usage in --comment, #4559 - use whitelist approach for borg serve, #4097 New features: - minimal native Windows support, see windows readme (work in progress) - create: first ctrl-c (SIGINT) triggers checkpoint and abort, #4606 - new BORG_WORKAROUNDS mechanism, basesyncfile, #4710 - remove WSL autodetection. if WSL still has this problem, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around it. - support xxh64 checksum in addition to the hashlib hashes in borg list - enable placeholder usage in all extra archive arguments - enable placeholder usage in --comment, #4559 - enable placeholder usage in --glob-archives, #4495 - ability to use a system-provided version of "xxhash" - create: - changed the default behaviour not to store the atime of fs items. atime is often rather not interesting and fragile - it easily changes even if nothing else has changed and, if stored into the archive, spoils deduplication of the archive metadata stream. - if you give the --noatime option, borg will output a deprecation warning because it is currently ignored / does nothing. Please remove the --noatime option when using borg 1.2. - added a --atime option for storing files' atime into an archive Other changes: - argparser: always use REPOSITORY in metavar - do not check python/libc for borg serve, #4483 - small borg compact improvements, #4522 - compact: log freed space at INFO level - tests: - tox / travis: add testing on py38-dev - fix broken test that relied on improper zlib assumptions - pure-py msgpack warning shall not make a lot of tests fail, #4558 - rename test_mount_hardlinks to test_fuse_mount_hardlinks (master) - vagrant: add up-to-date openindiana box (py35, openssl10) - get rid of confusing coverage warning, #2069 - docs: - reiterate that 'file cache names are absolute' in FAQ, mention bind mount solution, #4738 - add restore docs, #4670 - updated docs to cover use of temp directory on remote, #4545 - add a push-style example to borg-create(1), #4613 - timestamps in the files cache are now usually ctime, #4583 - benchmark crud: clarify that space is used until compact - update documentation of borg create, corrects a mention of borg 1.1 as a future version. - fix osxfuse github link in installation docs - how to supply a passphrase, use crypto devices, #4549 - extract: document limitation "needs empty destination", #4598 - update macOS Brew link - add note about software for automating backup - compact: improve docs, - README: new URL for funding options Version 1.2.0a6 (2019-04-22) ---------------------------- Fixes: - delete / prune: consider part files correctly for stats, #4507 - fix "all archives" stats considering part files, #4329 - create: only run stat_simple_attrs() once - create: --stats does not work with --dry-run, exit with error msg, #4373 - give "invalid repo" error msg if repo config not found, #4411 New features: - display msgpack version as part of sysinfo (e.g. in tracebacks) Other changes: - docs: - sdd "SSH Configuration" section, #4493, #3988, #636, #4485 - better document borg check --max-duration, #4473 - sorted commands help in multiple steps, #4471 - testing: - travis: use py 3.5.3 and 3.6.7 on macOS to get a pyenv-based python build with openssl 1.1 - vagrant: use py 3.5.3 and 3.6.8 on darwin64 VM to build python and borg with openssl 1.1 - pytest: -v and default XDISTN to 1, #4481 Version 1.2.0a5 (2019-03-21) ---------------------------- Fixes: - warn if a file has changed while being backed up, #1750 - lrucache: regularly remove old FDs, #4427 - borg command shall terminate with rc 2 for ImportErrors, #4424 - make freebsd xattr platform code api compatible with linux, #3952 Other changes: - major setup code refactoring (especially how libraries like openssl, liblz4, libzstd, libb2 are discovered and how it falls back to code bundled with borg), new: uses pkg-config now (and needs python "pkgconfig" package installed), #1925 if you are a borg package maintainer, please try packaging this (see comments in setup.py). - Vagrantfile: add zstd, reorder, build env vars, #4444 - travis: install script improvements - update shell completions - docs: - add a sample logging.conf in docs/misc, #4380 - fix spelling errors - update requirements / install docs, #4374 Version 1.2.0a4 (2019-03-11) ---------------------------- Fixes: - do not use O_NONBLOCK for special files, like FIFOs, block and char devices when using --read-special. fixes backing up FIFOs. fixes to test. #4394 - more LibreSSL build fixes: LibreSSL has HMAC_CTX_free and HMAC_CTX_new New features: - check: incremental repo check (only checks crc32 for segment entries), #1657 borg check --repository-only --max-duration SECONDS ... - delete: timestamp for borg delete --info added, #4359 Other changes: - redo stale lock handling, #3986 drop BORG_HOSTNAME_IS_UNIQUE (please use BORG_HOST_ID if needed). borg now always assumes it has a unique host id - either automatically from fqdn plus uuid.getnode() or overridden via BORG_HOST_ID. - docs: - added Alpine Linux to distribution list - elaborate on append-only mode docs - vagrant: - darwin: new 10.12 box - freebsd: new 12.0 box - openbsd: new 6.4 box - misc. updates / fixes Version 1.2.0a3 (2019-02-26) ---------------------------- Fixes: - LibreSSL build fixes, #4403 - dummy ACL/xattr code fixes (used by OpenBSD and others), #4403 - create: fix openat/statat issues for root directory, #4405 Version 1.2.0a2 and earlier (2019-02-24) ---------------------------------------- New features: - compact: "borg compact" needs to be used to free repository space by compacting the segments (reading sparse segments, rewriting still needed data to new segments, deleting the sparse segments). Borg < 1.2 invoked compaction automatically at the end of each repository writing command. Borg >= 1.2 does not do that any more to give better speed, more control, more segment file stability (== less stuff moving to newer segments) and more robustness. See the docs about "borg compact" for more details. - "borg compact --cleanup-commits" is to cleanup the tons of 17byte long commit-only segment files caused by borg 1.1.x issue #2850. Invoke this once after upgrading (the server side) borg to 1.2. Compaction now automatically removes unneeded commit-only segment files. - prune: Show which rule was applied to keep archive, #2886 - add fixed blocksize chunker (see --chunker-params docs), #1086 Fixes: - avoid stale filehandle issues, #3265 - use more FDs, avoid race conditions on active fs, #906, #908, #1038 - add O_NOFOLLOW to base flags, #908 - compact: - require >10% freeable space in a segment, #2985 - repository compaction now automatically removes unneeded 17byte commit-only segments, #2850 - make swidth available on all posix platforms, #2667 Other changes: - repository: better speed and less stuff moving around by using separate segment files for manifest DELETEs and PUTs, #3947 - use pyinstaller v3.3.1 to build binaries - update bundled zstd code to 1.3.8, #4210 - update bundled lz4 code to 1.8.3, #4209 - msgpack: - switch to recent "msgpack" pypi pkg name, #3890 - wrap msgpack to avoid future compat complications, #3632, #2738 - support msgpack 0.6.0 and 0.6.1, #4220, #4308 - llfuse: modernize / simplify llfuse version requirements - code refactorings / internal improvements: - include size/csize/nfiles[_parts] stats into archive, #3241 - calc_stats: use archive stats metadata, if available - crypto: refactored crypto to use an AEAD style API - crypto: new AES-OCB, CHACHA20-POLY1305 - create: use less syscalls by not using a python file obj, #906, #3962 - diff: refactor the diff functionality to new ItemDiff class, #2475 - archive: create FilesystemObjectProcessors class - helpers: make a package, split into smaller modules - xattrs: move to platform package, use cython instead ctypes, #2495 - xattrs/acls/bsdflags: misc. code/api optimizations - FUSE: separate creation of filesystem from implementation of llfuse funcs, #3042 - FUSE: use unpacker.tell() instead of deprecated write_bytes, #3899 - setup.py: move build_man / build_usage code to setup_docs.py - setup.py: update to use a newer Cython/setuptools API for compiling .pyx -> .c, #3788 - use python 3.5's os.scandir / os.set_blocking - multithreading preparations (not used yet): - item.to_optr(), Item.from_optr() - fix chunker holding the GIL during blocking I/O - C code portability / basic MSC compatibility, #4147, #2677 - testing: - vagrant: new VMs for linux/bsd/darwin, most with OpenSSL 1.1 and py36 Version 1.1.18 (2022-06-05) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. - 1.1.14 changes return codes due to a bug fix: In case you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128 (as documented since long). - 1.1.15 drops python 3.4 support, minimum requirement is 3.5 now. - 1.1.17 install_requires the "packaging" pypi package now. New features: - check --repair: significantly speed up search for next valid object in segment, #6022 - create: add retry_erofs workaround for O_NOATIME issue on volume shadow copies in WSL1, #6024 - key export: display key if path is '-' or not given, #6092 - list --format: add command_line to format keys, #6108 Fixes: - check: improve error handling for corrupt archive metadata block, make robust_iterator more robust, #4777 - diff: support presence change for blkdev, chrdev and fifo items, #6483 - diff: reduce memory consumption, fix is_hardlink_master - init: disallow overwriting of existing keyfiles - info: fix authenticated mode repo to show "Encrypted: No", #6462 - info: emit repo info even if repo has 0 archives, #6120 - list: remove placeholders for shake_* hashes, #6082 - mount -o versions: give clear error msg instead of crashing - show_progress: add finished=true/false to archive_progress json, #6570 - fix hardlinkable file type check, #6037 - do not show archive name in error msgs referring to the repository, #6023 - prettier error msg (no stacktrace) if exclude file is missing, #5734 - do not require BORG_CONFIG_DIR if BORG_{SECURITY,KEYS}_DIR are set, #5979 - atomically create the CACHE_TAG file, #6028 - deal with the SaveFile/SyncFile race, docs, see #6176 5c5b59bc9 - avoid expanding path into LHS of formatting operation + tests, #6064 #6063 - repository: quota / compactable computation fixes, #6119. This is mainly to keep the repo code in sync with borg 1.2. As borg 1.1 compacts immediately, there was not really an issue with this in 1.1. - fix transaction rollback: use files cache filename as found in txn.active, #6353 - do not load files cache for commands not using it, fixes #5673 - fix scp repo url parsing for ip v6 addrs, #6526 - repo::archive location placeholder expansion fixes, #5826, #5998 - use expanded location for log output - support placeholder expansion for BORG_REPO env var - respect umask for created directory and file modes, #6400 - safer truncate_and_unlink implementation Other changes: - upgrade bundled xxhash code to 0.8.1 - fix xxh64 related build (setup.py and post-0.8.1 patch for static_assert). The patch was required to build the bundled xxhash code on FreeBSD, see https://github.com/Cyan4973/xxHash/pull/670 - msgpack build: remove endianness macro, #6105 - update and fix shell completions - fuse: remove unneeded version check and compat code - delete --force: do not ask when deleting a repo, #5941 - delete: don't commit if nothing was deleted, avoid cache sync, #6060 - delete: add repository id and location to prompt - compact segments: improve freeable / freed space log output, #5679 - if ensure_dir() fails, give more informative error message, #5952 - load_key: no key is same as empty key, #6441 - better error msg for defect or unsupported repo configs, #6566 - use hmac.compare_digest instead of ==, #6470 - implement more standard hashindex.setdefault behaviour - remove stray punctuation from secure-erase message - add development.lock.txt, use a real python 3.5 to generate frozen reqs - setuptools 60.7.0 breaks pyinstaller, #6246 - setup.py clean2 was added to work around some setuptools customizability limitation. - allow extra compiler flags for every extension build - C code: make switch fallthrough explicit - Cython code: fix "useless trailing comma" cython warnings - requirements.lock.txt: use the latest cython 0.29.30 - fix compilation warnings: ‘PyUnicode_AsUnicode’ is deprecated - docs: - ~/.config/borg/keys is not used for repokey keys, #6107 - excluded parent dir's metadata can't restore, #6062 - permissions note rewritten to make it less confusing, #5490 - add note about grandfather-father-son backup retention policy / rotation scheme - clarify who starts the remote agent (borg serve) - test/improve pull backup docs, #5903 - document the socat pull mode described in #900 #515ß - borg serve: improve ssh forced commands docs, #6083 - improve docs for borg list --format, #6080 - fix the broken link to .nix file - clarify pattern usage with commands, #5176 - clarify user_id vs uid for fuse, #5723 - fix binary build freebsd/macOS version, #5942 - FAQ: fix manifest-timestamp path, #6016 - remove duplicate faq entries, #5926 - fix sphinx warnings, #5919 - virtualisation speed tips - fix values of TAG bytes, #6515 - recommend umask for passphrase file perms - update link to ubuntu packages, #6485 - clarify on-disk order and size of log entry fields, #6357 - do not transform --/--- to unicode dashes - improve linking inside docs, link to borg_placeholders, link to borg_patterns - use same phrasing in misc. help texts - borg init: explain the encryption modes better - explain the difference between a path that ends with or without a slash, #6297 - clarify usage of patternfile roots, #6242 - borg key export: add examples - updates about features not experimental any more: FUSE "versions" view, --pattern*, #6134 - fix/update cygwin package requirements - impact of deleting path/to/repo/nonce, #5858 - warn about tampered server nonce - mention BORG_FILES_CACHE_SUFFIX as alternative to BORG_FILES_CACHE_TTL, #5602 - add a troubleshooting note about "is not a valid repository" to the FAQ - vagrant / CI / testing: - misc. fixes and updates, new python versions - macOS on github: re-enable fuse2 testing by downgrading to older macOS, #6099 - fix OpenBSD symlink mode test failure, #2055 - use the generic/openbsd6 box - strengthen the test: we can read data w/o nonces - add tests for path/to/repo/nonce deletion - darwin64: backport some tunings from master - darwin64: remove fakeroot, #6314 - darwin64: fix vagrant scp, #5921 - darwin64: use macfuse instead of osxfuse - add ubuntu "jammy" 22.04 LTS VM - adapt memory for openindiana64 and darwin64 Version 1.1.17 (2021-07-12) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. - 1.1.14 changes return codes due to a bug fix: In case you have scripts expecting rc == 2 for a signal exit, you need to update them to check for >= 128 (as documented since long). - 1.1.15 drops python 3.4 support, minimum requirement is 3.5 now. - 1.1.17 install_requires the "packaging" pypi package now. Fixes: - pyinstaller dir-mode: fix pyi detection / LIBPATH treatment, #5897 - handle crash due to kill stale lock race, #5828 - fix BORG_CACHE_DIR crashing borg if empty, #5216 - create --dry-run: fix display of kept tagfile, #5834 - fix missing parameter in "did not consistently fail" msg, #5822 - missing / healed chunks: always tell chunk ID, #5704 - benchmark: make sure cleanup happens even on exceptions, #5630 New features: - implement BORG_SELFTEST env variable, #5871. this can be used to accelerate borg startup a bit. not recommended for normal usage, but borg mass hosters with a lot of borg invocations can save some resources with this. on my laptop, this saved ~100ms cpu time (sys+user) per borg command invocation. - implement BORG_LIBC env variable to give the libc filename, #5870. you can use this if a borg does not find your libc. - check: add progress indicator for archive check. - allow --files-cache=size (not recommended, make sure you know what you do) Other changes: - Python 3.10 now officially supported! we test on py310-dev on github CI since a while and now also on the vagrant machines, so it should work ok. - github CI: test on py310 (again) - get rid of distutils, use packaging and setuptools. distutils is deprecated and gives warnings on py 3.10. - setup.py: rename "clean" to "clean2" to avoid shadowing the "clean" command. - remove libc filename fallback for the BSDs (there is no "usual" name) - cleanup flake8 checks, fix some pep8 violations. - docs building: replace deprecated function ".add_stylesheet()" for Sphinx 4 compatibility - docs: - add a hint on sleeping computer and ssh connections, #5301 - update the documentation on hacked backup client, #5480 - improve docs/FAQ about append-only remote repos, #5497 - complement the documentation for pattern files and exclude files, #5520 - "filename with spaces" example added to exclude file, #5236 note: no whitespace escaping needed, processed by borg. - add info on renaming repositories, #5240 - clarify borg check --verify-data, #5808 - add notice about defective hardware to check documentation, #5753 - add paragraph added in #5855 to utility documentation source - add missing leading slashes in help patterns, #5857 - clarify "you will need key and passphrase" borg init warning, #4622 - pull mode: add some warnings, #5827 - mention tar --compare (compare archive to fs files), #5880 - fix typos, backport of #5597 - vagrant: - add py3.7.11 for binary build, also add 3.10-dev. - use latest Cython 0.29.23 for py310 compat fixes. - more RAM for openindiana upgrade plan resolver, it just hangs (swaps?) if there is too little RAM. - fix install_pyenv to adapt to recent changes in pyenv (same as in master now). - use generic/netbsd9 box, copied from master branch. Version 1.1.16 (2021-03-23) --------------------------- Fixes: - setup.py: add special openssl prefix for Apple M1 compatibility - do not recurse into duplicate roots, #5603 - remove empty shadowed_segments lists, #5275, #5614 - fix libpython load error when borg fat binary / dir-based binary is invoked via a symlink by upgrading pyinstaller to v4.2, #5688 - config: accept non-int value (like 500M or 100G) for max_segment_size or storage_quota, #5639. please note: when setting a non-int value for this in a repo config, using the repo will require borg >= 1.1.16. New features: - bundled msgpack: drop support for old buffer protocol to support Python 3.10 - verbose files cache logging via --debug-topic=files_cache, #5659. Use this if you suspect that borg does not detect unmodified files as expected. - create/extract: add --noxattrs and --noacls option, #3955. when given with borg create, borg will not get xattrs / ACLs from input files (and thus, it will not archive xattrs / ACLs). when given with borg extract, borg will not read xattrs / ACLs from archive and will not set xattrs / ACLs on extracted files. - diff: add --json-lines option, #3765 - check: debug log segment filename - borg debug dump-hints Other changes: - Tab completion support for additional archives for 'borg delete' - repository: deduplicate code of put and delete, no functional change - tests: fix result order issue (sporadic test failure on openindiana) - vagrant: - upgrade pyinstaller to v4.2, #5671 - avoid grub-install asking interactively for device - remove the xenial box - update freebsd box to 12.1 - docs: - update macOS install instructions, #5677 - use macFUSE (not osxfuse) for Apple M1 compatibility - update docs for dev environment installation instructions, #5643 - fix grammar in faq - recommend running tests only on installed versions for setup - add link back to git-installation - remove /var/cache exclusion in example commands, #5625. This is generally a poor idea and shouldn't be promoted through examples. - add repology.org badge with current packaging status - explain hash collision - add unsafe workaround to use an old repo copy, #5722 Version 1.1.15 (2020-12-25) --------------------------- Fixes: - extract: - improve exception handling when setting xattrs, #5092. - emit a warning message giving the path, xattr key and error message. - continue trying to restore other xattrs and bsdflags of the same file after an exception with xattr-setting happened. - export-tar: - set tar format to GNU_FORMAT explicitly, #5274 - fix memory leak with ssh: remote repository, #5568 - fix potential memory leak with ssh: remote repository with partial extraction - create: fix --dry-run and --stats coexistence, #5415 - use --timestamp for {utcnow} and {now} if given, #5189 New features: - create: implement --stdin-mode, --stdin-user and --stdin-group, #5333 - allow appending the files cache filename with BORG_FILES_CACHE_SUFFIX env var Other changes: - drop python 3.4 support, minimum requirement is 3.5 now. - enable using libxxhash instead of bundled xxh64 code - update llfuse requirements (1.3.8) - set cython language_level in some files to fix warnings - allow EIO with warning when trying to hardlink - PropDict: fail early if internal_dict is not a dict - update shell completions - tests / CI - add a test for the hashindex corruption bug, #5531 #4829 - fix spurious failure in test_cache_files, #5438 - added a github ci workflow - reduce testing on travis, no macOS, no py3x-dev, #5467 - travis: use newer dists, native py on dist - vagrant: - remove jessie and trusty boxes, #5348 #5383 - pyinstaller 4.0, build on py379 - binary build on stretch64, #5348 - remove easy_install based pip installation - docs: - clarify '--one-file-system' for btrfs, #5391 - add example for excluding content using the --pattern cmd line arg - complement the documentation for pattern files and exclude files, #5524 - made ansible playbook more generic, use package instead of pacman. also change state from "latest" to "present". - complete documentation on append-only remote repos, #5497 - internals: rather talk about target size than statistics, #5336 - new compression algorithm policy, #1633 #5505 - faq: add a hint on sleeping computer, #5301 - note requirements for full disk access on macOS Catalina, #5303 - fix/improve description of borg upgrade hardlink usage, #5518 - modernize 1.1 code: - drop code/workarounds only needed to support Python 3.4 - remove workaround for pre-release py37 argparse bug - removed some outdated comments/docstrings - requirements: remove some restrictions, lock on current versions Version 1.1.14 (2020-10-07) --------------------------- Fixes: - check --repair: fix potential data loss when interrupting it, #5325 - exit with 128 + signal number (as documented) when borg is killed by a signal, #5161 - fix hardlinked CACHEDIR.TAG processing, #4911 - create --read-special: .part files also should be regular files, #5217 - llfuse dependency: choose least broken 1.3.6/1.3.7. 1.3.6 is broken on python 3.9, 1.3.7 is broken on FreeBSD. Other changes: - upgrade bundled xxhash to 0.7.4 - self test: if it fails, also point to OS and hardware, #5334 - pyinstaller: compute basepath from spec file location - prettier error message when archive gets too big, #5307 - check/recreate are not "experimental" any more (but still potentially dangerous): - recreate: remove extra confirmation - rephrase some warnings, update docs, #5164 - shell completions: - misc. updates / fixes - support repositories in fish tab completion, #5256 - complete $BORG_RECREATE_I_KNOW_WHAT_I_AM_DOING - rewrite zsh completion: - completion for almost all optional and positional arguments - completion for Borg environment variables (parameters) - use "allow/deny list" instead of "white/black list" wording - declare "allow_cache_wipe" marker in setup.cfg to avoid pytest warning - vagrant / tests: - misc. fixes / updates - use python 3.5.10 for binary build - build directory-based binaries additionally to the single file binaries - add libffi-dev, required to build python - use cryptography<3.0, more recent versions break the jessie box - test on python 3.9 - do brew update with /dev/null redirect to avoid "too much log output" on travis-ci - docs: - add ssh-agent pull backup method docs, #5288 - how to approach borg speed issues, #5371 - mention double --force in prune docs - update Homebrew install instructions, #5185 - better description of how cache and rebuilds of it work - point to borg create --list item flags in recreate usage, #5165 - add security faq explaining AES-CTR crypto issues, #5254 - add a note to create from stdin regarding files cache, #5180 - fix borg.1 manpage generation regression, #5211 - clarify how exclude options work in recreate, #5193 - add section for retired contributors - hint about not misusing private email addresses of contributors for borg support Version 1.1.13 (2020-06-06) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. Fixes: - rebuilt using a current Cython version, compatible with python 3.8, #5214 Version 1.1.12 (2020-06-06) --------------------------- Fixes: - fix preload-related memory leak, #5202. - mount / borgfs (FUSE filesystem): - fix FUSE low linear read speed on large files, #5067 - fix crash on old llfuse without birthtime attrs, #5064 - accidentally we required llfuse >= 1.3. Now also old llfuse works again. - set f_namemax in statfs result, #2684 - update precedence of env vars to set config and cache paths, #4894 - correctly calculate compression ratio, taking header size into account, too New features: - --bypass-lock option to bypass locking with read-only repositories Other changes: - upgrade bundled zstd to 1.4.5 - travis: adding comments and explanations to Travis config / install script, improve macOS builds. - tests: test_delete_force: avoid sporadic test setup issues, #5196 - misc. vagrant fixes - the binary for macOS is now built on macOS 10.12 - the binaries for Linux are now built on Debian 8 "Jessie", #3761 - docs: - PlaceholderError not printed as JSON, #4073 - "How important is Borg config?", #4941 - make Sphinx warnings break docs build, #4587 - some markup / warning fixes - add "updating borgbackup.org/releases" to release checklist, #4999 - add "rendering docs" to release checklist, #5000 - clarify borg init's encryption modes - add note about patterns and stored paths, #4160 - add upgrade of tools to pip installation how-to - document one cause of orphaned chunks in check command, #2295 - linked recommended restrictions to ssh public keys on borg servers in faq, #4946 Version 1.1.11 (2020-03-08) --------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - 1.1.11 removes WSL autodetection (Windows 10 Subsystem for Linux). If WSL still has a problem with sync_file_range, you need to set BORG_WORKAROUNDS=basesyncfile in the borg process environment to work around the WSL issue. Fixes: - fixed potential index corruption / data loss issue due to bug in hashindex_set, #4829. Please read and follow the more detailed notes close to the top of this document. - upgrade bundled xxhash to 0.7.3, #4891. 0.7.2 is the minimum requirement for correct operations on ARMv6 in non-fixup mode, where unaligned memory accesses cause bus errors. 0.7.3 adds some speedups and libxxhash 0.7.3 even has a pkg-config file now. - upgrade bundled lz4 to 1.9.2 - upgrade bundled zstd to 1.4.4 - fix crash when upgrading erroneous hints file, #4922 - extract: - fix KeyError for "partial" extraction, #4607 - fix "partial" extract for hardlinked contentless file types, #4725 - fix preloading for old (0.xx) remote servers, #4652 - fix confusing output of borg extract --list --strip-components, #4934 - delete: after double-force delete, warn about necessary repair, #4704 - create: give invalid repo error msg if repo config not found, #4411 - mount: fix FUSE mount missing st_birthtime, #4763 #4767 - check: do not stumble over invalid item key, #4845 - info: if the archive doesn't exist, print a pretty message, #4793 - SecurityManager.known(): check all files, #4614 - Repository.open: use stat() to check for repo dir, #4695 - Repository.check_can_create_repository: use stat() to check, #4695 - fix invalid archive error message - fix optional/non-optional location arg, #4541 - commit-time free space calc: ignore bad compact map entries, #4796 - ignore EACCES (errno 13) when hardlinking the old config, #4730 - --prefix / -P: fix processing, avoid argparse issue, #4769 New features: - enable placeholder usage in all extra archive arguments - new BORG_WORKAROUNDS mechanism, basesyncfile, #4710 - recreate: support --timestamp option, #4745 - support platforms without os.link (e.g. Android with Termux), #4901. if we don't have os.link, we just extract another copy instead of making a hardlink. - support linux platforms without sync_file_range (e.g. Android 7 with Termux), #4905 Other: - ignore --stats when given with --dry-run, but continue, #4373 - add some ProgressIndicator msgids to code / fix docs, #4935 - elaborate on "Calculating size" message - argparser: always use REPOSITORY in metavar, also use more consistent help phrasing. - check: improve error output for matching index size, see #4829 - docs: - changelog: add advisory about hashindex_set bug #4829 - better describe BORG_SECURITY_DIR, BORG_CACHE_DIR, #4919 - infos about cache security assumptions, #4900 - add FAQ describing difference between a local repo vs. repo on a server. - document how to test exclusion patterns without performing an actual backup - timestamps in the files cache are now usually ctime, #4583 - fix bad reference to borg compact (does not exist in 1.1), #4660 - create: borg 1.1 is not future any more - extract: document limitation "needs empty destination", #4598 - how to supply a passphrase, use crypto devices, #4549 - fix osxfuse github link in installation docs - add example of exclude-norecurse rule in help patterns - update macOS Brew link - add note about software for automating backups, #4581 - AUTHORS: mention copyright+license for bundled msgpack - fix various code blocks in the docs, #4708 - updated docs to cover use of temp directory on remote, #4545 - add restore docs, #4670 - add a pull backup / push restore how-to, #1552 - add FAQ how to retain original paths, #4532 - explain difference between --exclude and --pattern, #4118 - add FAQs for SSH connection issues, #3866 - improve password FAQ, #4591 - reiterate that 'file cache names are absolute' in FAQ - tests: - cope with ANY error when importing pytest into borg.testsuite, #4652 - fix broken test that relied on improper zlib assumptions - test_fuse: filter out selinux xattrs, #4574 - travis / vagrant: - misc python versions removed / changed (due to openssl 1.1 compatibility) or added (3.7 and 3.8, for better borg compatibility testing) - binary building is on python 3.5.9 now - vagrant: - add new boxes: ubuntu 18.04 and 20.04, debian 10 - update boxes: openindiana, darwin, netbsd - remove old boxes: centos 6 - darwin: updated osxfuse to 3.10.4 - use debian/ubuntu pip/virtualenv packages - rather use python 3.6.2 than 3.6.0, fixes coverage/sqlite3 issue - use requirements.d/development.lock.txt to avoid compat issues - travis: - darwin: backport some install code / order from master - remove deprecated keyword "sudo" from travis config - allow osx builds to fail, #4955 this is due to travis-ci frequently being so slow that the OS X builds just fail because they exceed 50 minutes and get killed by travis. Version 1.1.10 (2019-05-16) --------------------------- Fixes: - extract: hang on partial extraction with ssh: repo, when hardlink master is not matched/extracted and borg hangs on related slave hardlink, #4350 - lrucache: regularly remove old FDs, #4427 - avoid stale filehandle issues, #3265 - freebsd: make xattr platform code api compatible with linux, #3952 - use whitelist approach for borg serve, #4097 - borg command shall terminate with rc 2 for ImportErrors, #4424 - create: only run stat_simple_attrs() once, this increases backup with lots of unchanged files performance by ~ 5%. - prune: fix incorrect borg prune --stats output with --dry-run, #4373 - key export: emit user-friendly error if repo key is exported to a directory, #4348 New features: - bundle latest supported msgpack-python release (0.5.6), remove msgpack-python from setup.py install_requires - by default we use the bundled code now. optionally, we still support using an external msgpack (see hints in setup.py), but this requires solid requirements management within distributions and is not recommended. borgbackup will break if you upgrade msgpack to an unsupported version. - display msgpack version as part of sysinfo (e.g. in tracebacks) - timestamp for borg delete --info added, #4359 - enable placeholder usage in --comment and --glob-archives, #4559, #4495 Other: - serve: do not check python/libc for borg serve, #4483 - shell completions: borg diff second archive - release scripts: signing binaries with Qubes OS support - testing: - vagrant: upgrade openbsd box to 6.4 - travis-ci: lock test env to py 3.4 compatible versions, #4343 - get rid of confusing coverage warning, #2069 - rename test_mount_hardlinks to test_fuse_mount_hardlinks, so both can be excluded by "not test_fuse". - pure-py msgpack warning shall not make a lot of tests fail, #4558 - docs: - add "SSH Configuration" section to "borg serve", #3988, #636, #4485 - README: new URL for funding options - add a sample logging.conf in docs/misc, #4380 - elaborate on append-only mode docs, #3504 - installation: added Alpine Linux to distribution list, #4415 - usage.html: only modify window.location when redirecting, #4133 - add msgpack license to docs/3rd_party/msgpack - vagrant / binary builds: - use python 3.5.7 for builds - use osxfuse 3.8.3 Version 1.1.9 (2019-02-10) -------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. Fixes: - security fix: configure FUSE with "default_permissions", #3903 "default_permissions" is now enforced by borg by default to let the kernel check uid/gid/mode based permissions. "ignore_permissions" can be given not to enforce "default_permissions". - make "hostname" short, even on misconfigured systems, #4262 - fix free space calculation on macOS (and others?), #4289 - config: quit with error message when no key is provided, #4223 - recover_segment: handle too small segment files correctly, #4272 - correctly release memoryview, #4243 - avoid diaper pattern in configparser by opening files, #4263 - add "# cython: language_level=3" directive to .pyx files, #4214 - info: consider part files for "This archive" stats, #3522 - work around Microsoft WSL issue #645 (sync_file_range), #1961 New features: - add --rsh command line option to complement BORG_RSH env var, #1701 - init: --make-parent-dirs parent1/parent2/repo_dir, #4235 Other: - add archive name to check --repair output, #3447 - check for unsupported msgpack versions - shell completions: - new shell completions for borg 1.1.9 - more complete shell completions for borg mount -o - added shell completions for borg help - option arguments for zsh tab completion - docs: - add FAQ regarding free disk space check, #3905 - update BORG_PASSCOMMAND example and clarify variable expansion, #4249 - FAQ regarding change of compression settings, #4222 - add note about BSD flags to changelog, #4246 - improve logging in example automation script - add note about files changing during backup, #4081 - work around the backslash issue, #4280 - update release workflow using twine (docs, scripts), #4213 - add warnings on repository copies to avoid future problems, #4272 - tests: - fix the homebrew 1.9 issues on travis-ci, #4254 - fix duplicate test method name, #4311 Version 1.1.8 (2018-12-09) -------------------------- Fixes: - enforce storage quota if set by serve-command, #4093 - invalid locations: give err msg containing parsed location, #4179 - list repo: add placeholders for hostname and username, #4130 - on linux, symlinks can't have ACLs, so don't try to set any, #4044 New features: - create: added PATH::archive output on INFO log level - read a passphrase from a file descriptor specified in the BORG_PASSPHRASE_FD environment variable. Other: - docs: - option --format is required for some expensive-to-compute values for json borg list by default does not compute expensive values except when they are needed. whether they are needed is determined by the format, in standard mode as well as in --json mode. - tell that our binaries are x86/x64 amd/intel, bauerj has ARM - fixed wrong archive name pattern in CRUD benchmark help - fixed link to cachedir spec in docs, #4140 - tests: - stop using fakeroot on travis, avoids sporadic EISDIR errors, #2482 - xattr key names must start with "user." on linux - fix code so flake8 3.6 does not complain - explicitly convert environment variable to str, #4136 - fix DeprecationWarning: Flags not at the start of the expression, #4137 - support pytest4, #4172 - vagrant: - use python 3.5.6 for builds Version 1.1.7 (2018-08-11) -------------------------- Compatibility notes: - added support for Python 3.7 Fixes: - cache lock: use lock_wait everywhere to fix infinite wait, see #3968 - don't archive tagged dir when recursing an excluded dir, #3991 - py37 argparse: work around bad default in py 3.7.0a/b/rc, #3996 - py37 remove loggerDict.clear() from tearDown method, #3805 - some fixes for bugs which likely did not result in problems in practice: - fixed logic bug in platform module API version check - fixed xattr/acl function prototypes, added missing ones New features: - init: add warning to store both key and passphrase at safe place(s) - BORG_HOST_ID env var to work around all-zero MAC address issue, #3985 - borg debug dump-repo-objs --ghost (dump everything from segment files, including deleted or superseded objects or commit tags) - borg debug search-repo-objs (search in repo objects for hex bytes or strings) Other changes: - add Python 3.7 support - updated shell completions - call socket.gethostname only once - locking: better logging, add some asserts - borg debug dump-repo-objs: - filename layout improvements - use repository.scan() to get on-disk order - docs: - update installation instructions for macOS - added instructions to install fuse via homebrew - improve diff docs - added note that checkpoints inside files requires 1.1+ - add link to tempfile module - remove row/column-spanning from docs source, #4000 #3990 - tests: - fetch less data via os.urandom - add py37 env for tox - travis: add 3.7, remove 3.6-dev (we test with -dev in master) - vagrant / binary builds: - use osxfuse 3.8.2 - use own (uptodate) openindiana box Version 1.1.6 (2018-06-11) -------------------------- Compatibility notes: - 1.1.6 changes: - also allow msgpack-python 0.5.6. Fixes: - fix borg exception handling on ENOSPC error with xattrs, #3808 - prune: fix/improve overall progress display - borg config repo ... does not need cache/manifest/key, #3802 - debug dump-repo-objs should not depend on a manifest obj - pypi package: - include .coveragerc, needed by tox.ini - fix package long description, #3854 New features: - mount: add uid, gid, umask mount options - delete: - only commit once, #3823 - implement --dry-run, #3822 - check: - show progress while rebuilding missing manifest, #3787 - more --repair output - borg config --list , #3612 Other changes: - update msgpack requirement, #3753 - update bundled zstd to 1.3.4, #3745 - update bundled lz4 code to 1.8.2, #3870 - docs: - describe what BORG_LIBZSTD_PREFIX does - fix and deduplicate encryption quickstart docs, #3776 - vagrant: - FUSE for macOS: upgrade 3.7.1 to 3.8.0 - exclude macOS High Sierra upgrade on the darwin64 machine - remove borgbackup.egg-info dir in fs_init (after rsync) - use pyenv-based build/test on jessie32/62 - use local 32 and 64bit debian jessie boxes - use "vagrant" as username for new xenial box - travis OS X: use xcode 8.3 (not broken) Version 1.1.5 (2018-04-01) -------------------------- Compatibility notes: - 1.1.5 changes: - require msgpack-python >= 0.4.6 and < 0.5.0. 0.5.0+ dropped python 3.4 testing and also caused some other issues because the python package was renamed to msgpack and emitted some FutureWarning. Fixes: - create --list: fix that it was never showing M status, #3492 - create: fix timing for first checkpoint (read files cache early, init checkpoint timer after that), see #3394 - extract: set rc=1 when extracting damaged files with all-zero replacement chunks or with size inconsistencies, #3448 - diff: consider an empty file as different to a non-existing file, #3688 - files cache: improve exception handling, #3553 - ignore exceptions in scandir_inorder() caused by an implicit stat(), also remove unneeded sort, #3545 - fixed tab completion problem where a space is always added after path even when it shouldn't - build: do .h file content checks in binary mode, fixes build issue for non-ascii header files on pure-ascii locale platforms, #3544 #3639 - borgfs: fix patterns/paths processing, #3551 - config: add some validation, #3566 - repository config: add validation for max_segment_size, #3592 - set cache previous_location on load instead of save - remove platform.uname() call which caused library mismatch issues, #3732 - add exception handler around deprecated platform.linux_distribution() call - use same datetime object for {now} and {utcnow}, #3548 New features: - create: implement --stdin-name, #3533 - add chunker_params to borg archive info (--json) - BORG_SHOW_SYSINFO=no to hide system information from exceptions Other changes: - updated zsh completions for borg 1.1.4 - files cache related code cleanups - be more helpful when parsing invalid --pattern values, #3575 - be more clear in secure-erase warning message, #3591 - improve getpass user experience, #3689 - docs build: unicode problem fixed when using a py27-based sphinx - docs: - security: explicitly note what happens OUTSIDE the attack model - security: add note about combining compression and encryption - security: describe chunk size / proximity issue, #3687 - quickstart: add note about permissions, borg@localhost, #3452 - quickstart: add introduction to repositories & archives, #3620 - recreate --recompress: add missing metavar, clarify description, #3617 - improve logging docs, #3549 - add an example for --pattern usage, #3661 - clarify path semantics when matching, #3598 - link to offline documentation from README, #3502 - add docs on how to verify a signed release with GPG, #3634 - chunk seed is generated per repository (not: archive) - better formatting of CPU usage documentation, #3554 - extend append-only repo rollback docs, #3579 - tests: - fix erroneously skipped zstd compressor tests, #3606 - skip a test if argparse is broken, #3705 - vagrant: - xenial64 box now uses username 'vagrant', #3707 - move cleanup steps to fs_init, #3706 - the boxcutter wheezy boxes are 404, use local ones - update to Python 3.5.5 (for binary builds) Version 1.1.4 (2017-12-31) -------------------------- Compatibility notes: - When upgrading from borg 1.0.x to 1.1.x, please note: - read all the compatibility notes for 1.1.0*, starting from 1.1.0b1. - borg upgrade: you do not need to and you also should not run it. - borg might ask some security-related questions once after upgrading. You can answer them either manually or via environment variable. One known case is if you use unencrypted repositories, then it will ask about a unknown unencrypted repository one time. - your first backup with 1.1.x might be significantly slower (it might completely read, chunk, hash a lot files) - this is due to the --files-cache mode change (and happens every time you change mode). You can avoid the one-time slowdown by using the pre-1.1.0rc4-compatible mode (but that is less safe for detecting changed files than the default). See the --files-cache docs for details. - borg 1.1.4 changes: - zstd compression is new in borg 1.1.4, older borg can't handle it. - new minimum requirements for the compression libraries - if the required versions (header and lib) can't be found at build time, bundled code will be used: - added requirement: libzstd >= 1.3.0 (bundled: 1.3.2) - updated requirement: liblz4 >= 1.7.0 / r129 (bundled: 1.8.0) Fixes: - check: data corruption fix: fix for borg check --repair malfunction, #3444. See the more detailed notes close to the top of this document. - delete: also delete security dir when deleting a repo, #3427 - prune: fix building the "borg prune" man page, #3398 - init: use given --storage-quota for local repo, #3470 - init: properly quote repo path in output - fix startup delay with dns-only own fqdn resolving, #3471 New features: - added zstd compression. try it! - added placeholder {reverse-fqdn} for fqdn in reverse notation - added BORG_BASE_DIR environment variable, #3338 Other changes: - list help topics when invalid topic is requested - fix lz4 deprecation warning, requires lz4 >= 1.7.0 (r129) - add parens for C preprocessor macro argument usages (did not cause malfunction) - exclude broken pytest 3.3.0 release - updated fish/bash completions - init: more clear exception messages for borg create, #3465 - docs: - add auto-generated docs for borg config - don't generate HTML docs page for borgfs, #3404 - docs update for lz4 b2 zstd changes - add zstd to compression help, readme, docs - update requirements and install docs about bundled lz4 and zstd - refactored build of the compress and crypto.low_level extensions, #3415: - move some lib/build related code to setup_{zstd,lz4,b2}.py - bundle lz4 1.8.0 (requirement: >= 1.7.0 / r129) - bundle zstd 1.3.2 (requirement: >= 1.3.0) - blake2 was already bundled - rename BORG_LZ4_PREFIX env var to BORG_LIBLZ4_PREFIX for better consistency: we also have BORG_LIBB2_PREFIX and BORG_LIBZSTD_PREFIX now. - add prefer_system_lib* = True settings to setup.py - by default the build will prefer a shared library over the bundled code, if library and headers can be found and meet the minimum requirements. Version 1.1.3 (2017-11-27) -------------------------- Fixes: - Security Fix for CVE-2017-15914: Incorrect implementation of access controls allows remote users to override repository restrictions in Borg servers. A user able to access a remote Borg SSH server is able to circumvent access controls post-authentication. Affected releases: 1.1.0, 1.1.1, 1.1.2. Releases 1.0.x are NOT affected. - crc32: deal with unaligned buffer, add tests - this broke borg on older ARM CPUs that can not deal with unaligned 32bit memory accesses and raise a bus error in such cases. the fix might also improve performance on some CPUs as all 32bit memory accesses by the crc32 code are properly aligned now. #3317 - mount: fixed support of --consider-part-files and do not show .borg_part_N files by default in the mounted FUSE filesystem. #3347 - fixed cache/repo timestamp inconsistency message, highlight that information is obtained from security dir (deleting the cache will not bypass this error in case the user knows this is a legitimate repo). - borgfs: don't show sub-command in borgfs help, #3287 - create: show an error when --dry-run and --stats are used together, #3298 New features: - mount: added exclusion group options and paths, #2138 Reused some code to support similar options/paths as borg extract offers - making good use of these to mount only a smaller subset of dirs/files can speed up mounting a lot and also will consume way less memory. borg mount [options] repo_or_archive mountpoint path [paths...] paths: you can just give some "root paths" (like for borg extract) to only partially populate the FUSE filesystem. new options: --exclude[-from], --pattern[s-from], --strip-components - create/extract: support st_birthtime on platforms supporting it, #3272 - add "borg config" command for querying/setting/deleting config values, #3304 Other changes: - clean up and simplify packaging (only package committed files, do not install .c/.h/.pyx files) - docs: - point out tuning options for borg create, #3239 - add instructions for using ntfsclone, zerofree, #81 - move image backup-related FAQ entries to a new page - clarify key aliases for borg list --format, #3111 - mention break-lock in checkpointing FAQ entry, #3328 - document sshfs rename workaround, #3315 - add FAQ about removing files from existing archives - add FAQ about different prune policies - usage and man page for borgfs, #3216 - clarify create --stats duration vs. wall time, #3301 - clarify encrypted key format for borg key export, #3296 - update release checklist about security fixes - document good and problematic option placements, fix examples, #3356 - add note about using --nobsdflags to avoid speed penalty related to bsdflags, #3239 - move most of support section to www.borgbackup.org Version 1.1.2 (2017-11-05) -------------------------- Fixes: - fix KeyError crash when talking to borg server < 1.0.7, #3244 - extract: set bsdflags last (include immutable flag), #3263 - create: don't do stat() call on excluded-norecurse directory, fix exception handling for stat() call, #3209 - create --stats: do not count data volume twice when checkpointing, #3224 - recreate: move chunks_healthy when excluding hardlink master, #3228 - recreate: get rid of chunks_healthy when rechunking (does not match), #3218 - check: get rid of already existing not matching chunks_healthy metadata, #3218 - list: fix stdout broken pipe handling, #3245 - list/diff: remove tag-file options (not used), #3226 New features: - bash, zsh and fish shell auto-completions, see scripts/shell_completions/ - added BORG_CONFIG_DIR env var, #3083 Other changes: - docs: - clarify using a blank passphrase in keyfile mode - mention "!" (exclude-norecurse) type in "patterns" help - document to first heal before running borg recreate to re-chunk stuff, because that will have to get rid of chunks_healthy metadata. - more than 23 is not supported for CHUNK_MAX_EXP, #3115 - borg does not respect nodump flag by default any more - clarify same-filesystem requirement for borg upgrade, #2083 - update / rephrase cygwin / WSL status, #3174 - improve docs about --stats, #3260 - vagrant: openindiana new clang package Already contained in 1.1.1 (last minute fix): - arg parsing: fix fallback function, refactor, #3205. This is a fixup for #3155, which was broken on at least python <= 3.4.2. Version 1.1.1 (2017-10-22) -------------------------- Compatibility notes: - The deprecated --no-files-cache is not a global/common option any more, but only available for borg create (it is not needed for anything else). Use --files-cache=disabled instead of --no-files-cache. - The nodump flag ("do not back up this file") is not honoured any more by default because this functionality (esp. if it happened by error or unexpected) was rather confusing and unexplainable at first to users. If you want that "do not back up NODUMP-flagged files" behaviour, use: borg create --exclude-nodump ... - If you are on Linux and do not need bsdflags archived, consider using ``--nobsdflags`` with ``borg create`` to avoid additional syscalls and speed up backup creation. Fixes: - borg recreate: correctly compute part file sizes. fixes cosmetic, but annoying issue as borg check complains about size inconsistencies of part files in affected archives. you can solve that by running borg recreate on these archives, see also #3157. - bsdflags support: do not open BLK/CHR/LNK files, avoid crashes and slowness, #3130 - recreate: don't crash on attic archives w/o time_end, #3109 - don't crash on repository filesystems w/o hardlink support, #3107 - don't crash in first part of truncate_and_unlink, #3117 - fix server-side IndexError crash with clients < 1.0.7, #3192 - don't show traceback if only a global option is given, show help, #3142 - cache: use SaveFile for more safety, #3158 - init: fix wrong encryption choices in command line parser, fix missing "authenticated-blake2", #3103 - move --no-files-cache from common to borg create options, #3146 - fix detection of non-local path (failed on ..filename), #3108 - logging with fileConfig: set json attr on "borg" logger, #3114 - fix crash with relative BORG_KEY_FILE, #3197 - show excluded dir with "x" for tagged dirs / caches, #3189 New features: - create: --nobsdflags and --exclude-nodump options, #3160 - extract: --nobsdflags option, #3160 Other changes: - remove annoying hardlinked symlinks warning, #3175 - vagrant: use self-made FreeBSD 10.3 box, #3022 - travis: don't brew update, hopefully fixes #2532 - docs: - readme: -e option is required in borg 1.1 - add example showing --show-version --show-rc - use --format rather than --list-format (deprecated) in example - update docs about hardlinked symlinks limitation Version 1.1.0 (2017-10-07) -------------------------- Compatibility notes: - borg command line: do not put options in between positional arguments This sometimes works (e.g. it worked in borg 1.0.x), but can easily stop working if we make positional arguments optional (like it happened for borg create's "paths" argument in 1.1). There are also places in borg 1.0 where we do that, so it doesn't work there in general either. #3356 Good: borg create -v --stats repo::archive path Good: borg create repo::archive path -v --stats Bad: borg create repo::archive -v --stats path Fixes: - fix LD_LIBRARY_PATH restoration for subprocesses, #3077 - "auto" compression: make sure expensive compression is actually better, otherwise store lz4 compressed data we already computed. Other changes: - docs: - FAQ: we do not implement futile attempts of ETA / progress displays - manpage: fix typos, update homepage - implement simple "issue" role for manpage generation, #3075 Version 1.1.0rc4 (2017-10-01) ----------------------------- Compatibility notes: - A borg server >= 1.1.0rc4 does not support borg clients 1.1.0b3-b5. #3033 - The files cache is now controlled differently and has a new default mode: - the files cache now uses ctime by default for improved file change detection safety. You can still use mtime for more speed and less safety. - --ignore-inode is deprecated (use --files-cache=... without "inode") - --no-files-cache is deprecated (use --files-cache=disabled) New features: - --files-cache - implement files cache mode control, #911 You can now control the files cache mode using this option: --files-cache={ctime,mtime,size,inode,rechunk,disabled} (only some combinations are supported). See the docs for details. Fixes: - remote progress/logging: deal with partial lines, #2637 - remote progress: flush json mode output - fix subprocess environments, #3050 (and more) Other changes: - remove client_supports_log_v3 flag, #3033 - exclude broken Cython 0.27(.0) in requirements, #3066 - vagrant: - upgrade to FUSE for macOS 3.7.1 - use Python 3.5.4 to build the binaries - docs: - security: change-passphrase only changes the passphrase, #2990 - fixed/improved borg create --compression examples, #3034 - add note about metadata dedup and --no[ac]time, #2518 - twitter account @borgbackup now, better visible, #2948 - simplified rate limiting wrapper in FAQ Version 1.1.0rc3 (2017-09-10) ----------------------------- New features: - delete: support naming multiple archives, #2958 Fixes: - repo cleanup/write: invalidate cached FDs, #2982 - fix datetime.isoformat() microseconds issues, #2994 - recover_segment: use mmap(), lower memory needs, #2987 Other changes: - with-lock: close segment file before invoking subprocess - keymanager: don't depend on optional readline module, #2976 - docs: - fix macOS keychain integration command - show/link new screencasts in README, #2936 - document utf-8 locale requirement for json mode, #2273 - vagrant: clean up shell profile init, user name, #2977 - test_detect_attic_repo: don't test mount, #2975 - add debug logging for repository cleanup Version 1.1.0rc2 (2017-08-28) ----------------------------- Compatibility notes: - list: corrected mix-up of "isomtime" and "mtime" formats. Previously, "isomtime" was the default but produced a verbose human format, while "mtime" produced a ISO-8601-like format. The behaviours have been swapped (so "mtime" is human, "isomtime" is ISO-like), and the default is now "mtime". "isomtime" is now a real ISO-8601 format ("T" between date and time, not a space). New features: - None. Fixes: - list: fix weird mixup of mtime/isomtime - create --timestamp: set start time, #2957 - ignore corrupt files cache, #2939 - migrate locks to child PID when daemonize is used - fix exitcode of borg serve, #2910 - only compare contents when chunker params match, #2899 - umount: try fusermount, then try umount, #2863 Other changes: - JSON: use a more standard ISO 8601 datetime format, #2376 - cache: write_archive_index: truncate_and_unlink on error, #2628 - detect non-upgraded Attic repositories, #1933 - delete various nogil and threading related lines - coala / pylint related improvements - docs: - renew asciinema/screencasts, #669 - create: document exclusion through nodump, #2949 - minor formatting fixes - tar: tarpipe example - improve "with-lock" and "info" docs, #2869 - detail how to use macOS/GNOME/KDE keyrings for repo passwords, #392 - travis: only short-circuit docs-only changes for pull requests - vagrant: - netbsd: bash is already installed - fix netbsd version in PKG_PATH - add exe location to PATH when we build an exe Version 1.1.0rc1 (2017-07-24) ----------------------------- Compatibility notes: - delete: removed short option for --cache-only New features: - support borg list repo --format {comment} {bcomment} {end}, #2081 - key import: allow reading from stdin, #2760 Fixes: - with-lock: avoid creating segment files that might be overwritten later, #1867 - prune: fix checkpoints processing with --glob-archives - FUSE: versions view: keep original file extension at end, #2769 - fix --last, --first: do not accept values <= 0, fix reversed archive ordering with --last - include testsuite data (attic.tar.gz) when installing the package - use limited unpacker for outer key, for manifest (both security precautions), #2174 #2175 - fix bashism in shell scripts, #2820, #2816 - cleanup endianness detection, create _endian.h, fixes build on alpine linux, #2809 - fix crash with --no-cache-sync (give known chunk size to chunk_incref), #2853 Other changes: - FUSE: versions view: linear numbering by archive time - split up interval parsing from filtering for --keep-within, #2610 - add a basic .editorconfig, #2734 - use archive creation time as mtime for FUSE mount, #2834 - upgrade FUSE for macOS (osxfuse) from 3.5.8 to 3.6.3, #2706 - hashindex: speed up by replacing modulo with "if" to check for wraparound - coala checker / pylint: fixed requirements and .coafile, more ignores - borg upgrade: name backup directories as 'before-upgrade', #2811 - add .mailmap - some minor changes suggested by lgtm.com - docs: - better explanation of the --ignore-inode option relevance, #2800 - fix openSUSE command and add openSUSE section - simplify ssh authorized_keys file using "restrict", add legacy note, #2121 - mount: show usage of archive filters - mount: add repository example, #2462 - info: update and add examples, #2765 - prune: include example - improved style / formatting - improved/fixed segments_per_dir docs - recreate: fix wrong "remove unwanted files" example - reference list of status chars in borg recreate --filter description - update source-install docs about doc build dependencies, #2795 - cleanup installation docs - file system requirements, update segs per dir - fix checkpoints/parts reference in FAQ, #2859 - code: - hashindex: don't pass side effect into macro - crypto low_level: don't mutate local bytes() - use dash_open function to open file or "-" for stdin/stdout - archiver: argparse cleanup / refactoring - shellpattern: add match_end arg - tests: added some additional unit tests, some fixes, #2700 #2710 - vagrant: fix setup of cygwin, add Debian 9 "stretch" - travis: don't perform full travis build on docs-only changes, #2531 Version 1.1.0b6 (2017-06-18) ---------------------------- Compatibility notes: - Running "borg init" via a "borg serve --append-only" server will *not* create an append-only repository anymore. Use "borg init --append-only" to initialize an append-only repository. - Repositories in the "repokey" and "repokey-blake2" modes with an empty passphrase are now treated as unencrypted repositories for security checks (e.g. BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK). Previously there would be no prompts nor messages if an unknown repository in one of these modes with an empty passphrase was encountered. This would allow an attacker to swap a repository, if one assumed that the lack of password prompts was due to a set BORG_PASSPHRASE. Since the "trick" does not work if BORG_PASSPHRASE is set, this does generally not affect scripts. - Repositories in the "authenticated" mode are now treated as the unencrypted repositories they are. - The client-side temporary repository cache now holds unencrypted data for better speed. - borg init: removed the short form of --append-only (-a). - borg upgrade: removed the short form of --inplace (-i). New features: - reimplemented the RepositoryCache, size-limited caching of decrypted repo contents, integrity checked via xxh64. #2515 - reduced space usage of chunks.archive.d. Existing caches are migrated during a cache sync. #235 #2638 - integrity checking using xxh64 for important files used by borg, #1101: - repository: index and hints files - cache: chunks and files caches, chunks.archive.d - improve cache sync speed, #1729 - create: new --no-cache-sync option - add repository mandatory feature flags infrastructure, #1806 - Verify most operations against SecurityManager. Location, manifest timestamp and key types are now checked for almost all non-debug commands. #2487 - implement storage quotas, #2517 - serve: add --restrict-to-repository, #2589 - BORG_PASSCOMMAND: use external tool providing the key passphrase, #2573 - borg export-tar, #2519 - list: --json-lines instead of --json for archive contents, #2439 - add --debug-profile option (and also "borg debug convert-profile"), #2473 - implement --glob-archives/-a, #2448 - normalize authenticated key modes for better naming consistency: - rename "authenticated" to "authenticated-blake2" (uses blake2b) - implement "authenticated" mode (uses hmac-sha256) Fixes: - hashindex: read/write indices >2 GiB on 32bit systems, better error reporting, #2496 - repository URLs: implement IPv6 address support and also more informative error message when parsing fails. - mount: check whether llfuse is installed before asking for passphrase, #2540 - mount: do pre-mount checks before opening repository, #2541 - FUSE: - fix crash if empty (None) xattr is read, #2534 - fix read(2) caching data in metadata cache - fix negative uid/gid crash (fix crash when mounting archives of external drives made on cygwin), #2674 - redo ItemCache, on top of object cache - use decrypted cache - remove unnecessary normpaths - serve: ignore --append-only when initializing a repository (borg init), #2501 - serve: fix incorrect type of exception_short for Errors, #2513 - fix --exclude and --exclude-from recursing into directories, #2469 - init: don't allow creating nested repositories, #2563 - --json: fix encryption[mode] not being the cmdline name - remote: propagate Error.traceback correctly - fix remote logging and progress, #2241 - implement --debug-topic for remote servers - remote: restore "Remote:" prefix (as used in 1.0.x) - rpc negotiate: enable v3 log protocol only for supported clients - fix --progress and logging in general for remote - fix parse_version, add tests, #2556 - repository: truncate segments (and also some other files) before unlinking, #2557 - recreate: keep timestamps as in original archive, #2384 - recreate: if single archive is not processed, exit 2 - patterns: don't recurse with ! / --exclude for pf:, #2509 - cache sync: fix n^2 behaviour in lookup_name - extract: don't write to disk with --stdout (affected non-regular-file items), #2645 - hashindex: implement KeyError, more tests Other changes: - remote: show path in PathNotAllowed - consider repokey w/o passphrase == unencrypted, #2169 - consider authenticated mode == unencrypted, #2503 - restrict key file names, #2560 - document follow_symlinks requirements, check libc, use stat and chown with follow_symlinks=False, #2507 - support common options on the main command, #2508 - support common options on mid-level commands (e.g. borg *key* export) - make --progress a common option - increase DEFAULT_SEGMENTS_PER_DIR to 1000 - chunker: fix invalid use of types (function only used by tests) - chunker: don't do uint32_t >> 32 - FUSE: - add instrumentation (--debug and SIGUSR1/SIGINFO) - reduced memory usage for repository mounts by lazily instantiating archives - improved archive load times - info: use CacheSynchronizer & HashIndex.stats_against (better performance) - docs: - init: document --encryption as required - security: OpenSSL usage - security: used implementations; note python libraries - security: security track record of OpenSSL and msgpack - patterns: document denial of service (regex, wildcards) - init: note possible denial of service with "none" mode - init: document SHA extension is supported in OpenSSL and thus SHA is faster on AMD Ryzen than blake2b. - book: use A4 format, new builder option format. - book: create appendices - data structures: explain repository compaction - data structures: add chunk layout diagram - data structures: integrity checking - data structures: demingle cache and repo index - Attic FAQ: separate section for attic stuff - FAQ: I get an IntegrityError or similar - what now? - FAQ: Can I use Borg on SMR hard drives?, #2252 - FAQ: specify "using inline shell scripts" - add systemd warning regarding placeholders, #2543 - xattr: document API - add docs/misc/borg-data-flow data flow chart - debugging facilities - README: how to help the project, #2550 - README: add bountysource badge, #2558 - fresh new theme + tweaking - logo: vectorized (PDF and SVG) versions - frontends: use headlines - you can link to them - mark --pattern, --patterns-from as experimental - highlight experimental features in online docs - remove regex based pattern examples, #2458 - nanorst for "borg help TOPIC" and --help - split deployment - deployment: hosting repositories - deployment: automated backups to a local hard drive - development: vagrant, windows10 requirements - development: update docs remarks - split usage docs, #2627 - usage: avoid bash highlight, [options] instead of - usage: add benchmark page - helpers: truncate_and_unlink doc - don't suggest to leak BORG_PASSPHRASE - internals: columnize rather long ToC [webkit fixup] internals: manifest & feature flags - internals: more HashIndex details - internals: fix ASCII art equations - internals: edited obj graph related sections a bit - internals: layers image + description - fix way too small figures in pdf - index: disable syntax highlight (bash) - improve options formatting, fix accidental block quotes - testing / checking: - add support for using coala, #1366 - testsuite: add ArchiverCorruptionTestCase - do not test logger name, #2504 - call setup_logging after destroying logging config - testsuite.archiver: normalise pytest.raises vs. assert_raises - add test for preserved intermediate folder permissions, #2477 - key: add round-trip test - remove attic dependency of the tests, #2505 - enable remote tests on cygwin - tests: suppress tar's future timestamp warning - cache sync: add more refcount tests - repository: add tests, including corruption tests - vagrant: - control VM cpus and pytest workers via env vars VMCPUS and XDISTN - update cleaning workdir - fix openbsd shell - add OpenIndiana - packaging: - binaries: don't bundle libssl - setup.py clean to remove compiled files - fail in borg package if version metadata is very broken (setuptools_scm) - repo / code structure: - create borg.algorithms and borg.crypto packages - algorithms: rename crc32 to checksums - move patterns to module, #2469 - gitignore: complete paths for src/ excludes - cache: extract CacheConfig class - implement IntegrityCheckedFile + Detached variant, #2502 #1688 - introduce popen_with_error_handling to handle common user errors Version 1.1.0b5 (2017-04-30) ---------------------------- Compatibility notes: - BORG_HOSTNAME_IS_UNIQUE is now on by default. - removed --compression-from feature - recreate: add --recompress flag, unify --always-recompress and --recompress Fixes: - catch exception for os.link when hardlinks are not supported, #2405 - borg rename / recreate: expand placeholders, #2386 - generic support for hardlinks (files, devices, FIFOs), #2324 - extract: also create parent dir for device files, if needed, #2358 - extract: if a hardlink master is not in the to-be-extracted subset, the "x" status was not displayed for it, #2351 - embrace y2038 issue to support 32bit platforms: clamp timestamps to int32, #2347 - verify_data: fix IntegrityError handling for defect chunks, #2442 - allow excluding parent and including child, #2314 Other changes: - refactor compression decision stuff - change global compression default to lz4 as well, to be consistent with --compression defaults. - placeholders: deny access to internals and other unspecified stuff - clearer error message for unrecognized placeholder - more clear exception if borg check does not help, #2427 - vagrant: upgrade FUSE for macOS to 3.5.8, #2346 - linux binary builds: get rid of glibc 2.13 dependency, #2430 - docs: - placeholders: document escaping - serve: env vars in original commands are ignored - tell what kind of hardlinks we support - more docs about compression - LICENSE: use canonical formulation ("copyright holders and contributors" instead of "author") - document borg init behaviour via append-only borg serve, #2440 - be clear about what buzhash is used for, #2390 - add hint about chunker params, #2421 - clarify borg upgrade docs, #2436 - FAQ to explain warning when running borg check --repair, #2341 - repository file system requirements, #2080 - pre-install considerations - misc. formatting / crossref fixes - tests: - enhance travis setuptools_scm situation - add extra test for the hashindex - fix invalid param issue in benchmarks These belong to 1.1.0b4 release, but did not make it into changelog by then: - vagrant: increase memory for parallel testing - lz4 compress: lower max. buffer size, exception handling - add docstring to do_benchmark_crud - patterns help: mention path full-match in intro Version 1.1.0b4 (2017-03-27) ---------------------------- Compatibility notes: - init: the --encryption argument is mandatory now (there are several choices) - moved "borg migrate-to-repokey" to "borg key migrate-to-repokey". - "borg change-passphrase" is deprecated, use "borg key change-passphrase" instead. - the --exclude-if-present option now supports tagging a folder with any filesystem object type (file, folder, etc), instead of expecting only files as tags, #1999 - the --keep-tag-files option has been deprecated in favor of the new --keep-exclude-tags, to account for the change mentioned above. - use lz4 compression by default, #2179 New features: - JSON API to make developing frontends and automation easier (see :ref:`json_output`) - add JSON output to commands: `borg create/list/info --json ...`. - add --log-json option for structured logging output. - add JSON progress information, JSON support for confirmations (yes()). - add two new options --pattern and --patterns-from as discussed in #1406 - new path full match pattern style (pf:) for very fast matching, #2334 - add 'debug dump-manifest' and 'debug dump-archive' commands - add 'borg benchmark crud' command, #1788 - new 'borg delete --force --force' to delete severely corrupted archives, #1975 - info: show utilization of maximum archive size, #1452 - list: add dsize and dcsize keys, #2164 - paperkey.html: Add interactive html template for printing key backups. - key export: add qr html export mode - securely erase config file (which might have old encryption key), #2257 - archived file items: add size to metadata, 'borg extract' and 'borg check' do check the file size for consistency, FUSE uses precomputed size from Item. Fixes: - fix remote speed regression introduced in 1.1.0b3, #2185 - fix regression handling timestamps beyond 2262 (revert bigint removal), introduced in 1.1.0b3, #2321 - clamp (nano)second values to unproblematic range, #2304 - hashindex: rebuild hashtable if we have too little empty buckets (performance fix), #2246 - Location regex: fix bad parsing of wrong syntax - ignore posix_fadvise errors in repository.py, #2095 - borg rpc: use limited msgpack.Unpacker (security precaution), #2139 - Manifest: Make sure manifest timestamp is strictly monotonically increasing. - create: handle BackupOSError on a per-path level in one spot - create: clarify -x option / meaning of "same filesystem" - create: don't create hard link refs to failed files - archive check: detect and fix missing all-zero replacement chunks, #2180 - files cache: update inode number when --ignore-inode is used, #2226 - fix decompression exceptions crashing ``check --verify-data`` and others instead of reporting integrity error, #2224 #2221 - extract: warning for unextracted big extended attributes, #2258, #2161 - mount: umount on SIGINT/^C when in foreground - mount: handle invalid hard link refs - mount: fix huge RAM consumption when mounting a repository (saves number of archives * 8 MiB), #2308 - hashindex: detect mingw byte order #2073 - hashindex: fix wrong skip_hint on hashindex_set when encountering tombstones, the regression was introduced in #1748 - fix ChunkIndex.__contains__ assertion for big-endian archs - fix borg key/debug/benchmark crashing without subcommand, #2240 - Location: accept //servername/share/path - correct/refactor calculation of unique/non-unique chunks - extract: fix missing call to ProgressIndicator.finish - prune: fix error msg, it is --keep-within, not --within - fix "auto" compression mode bug (not compressing), #2331 - fix symlink item fs size computation, #2344 Other changes: - remote repository: improved async exception processing, #2255 #2225 - with --compression auto,C, only use C if lz4 achieves at least 3% compression - PatternMatcher: only normalize path once, #2338 - hashindex: separate endian-dependent defs from endian detection - migrate-to-repokey: ask using canonical_path() as we do everywhere else. - SyncFile: fix use of fd object after close - make LoggedIO.close_segment reentrant - creating a new segment: use "xb" mode, #2099 - redo key_creator, key_factory, centralise key knowledge, #2272 - add return code functions, #2199 - list: only load cache if needed - list: files->items, clarifications - list: add "name" key for consistency with info cmd - ArchiveFormatter: add "start" key for compatibility with "info" - RemoteRepository: account rx/tx bytes - setup.py build_usage/build_man/build_api fixes - Manifest.in: simplify, exclude .so, .dll and .orig, #2066 - FUSE: get rid of chunk accounting, st_blocks = ceil(size / blocksize). - tests: - help python development by testing 3.6-dev - test for borg delete --force - vagrant: - freebsd: some fixes, #2067 - darwin64: use osxfuse 3.5.4 for tests / to build binaries - darwin64: improve VM settings - use python 3.5.3 to build binaries, #2078 - upgrade pyinstaller from 3.1.1+ to 3.2.1 - pyinstaller: use fixed AND freshly compiled bootloader, #2002 - pyinstaller: automatically builds bootloader if missing - docs: - create really nice man pages - faq: mention --remote-ratelimit in bandwidth limit question - fix caskroom link, #2299 - docs/security: reiterate that RPC in Borg does no networking - docs/security: counter tracking, #2266 - docs/development: update merge remarks - address SSH batch mode in docs, #2202 #2270 - add warning about running build_usage on Python >3.4, #2123 - one link per distro in the installation page - improve --exclude-if-present and --keep-exclude-tags, #2268 - improve automated backup script in doc, #2214 - improve remote-path description - update docs for create -C default change (lz4) - document relative path usage, #1868 - document snapshot usage, #2178 - corrected some stuff in internals+security - internals: move toctree to after the introduction text - clarify metadata kind, manifest ops - key enc: correct / clarify some stuff, link to internals/security - datas: enc: 1.1.x mas different MACs - datas: enc: correct factual error -- no nonce involved there. - make internals.rst an index page and edit it a bit - add "Cryptography in Borg" and "Remote RPC protocol security" sections - document BORG_HOSTNAME_IS_UNIQUE, #2087 - FAQ by categories as proposed by @anarcat in #1802 - FAQ: update Which file types, attributes, etc. are *not* preserved? - development: new branching model for git repository - development: define "ours" merge strategy for auto-generated files - create: move --exclude note to main doc - create: move item flags to main doc - fix examples using borg init without -e/--encryption - list: don't print key listings in fat (html + man) - remove Python API docs (were very incomplete, build problems on RTFD) - added FAQ section about backing up root partition Version 1.1.0b3 (2017-01-15) ---------------------------- Compatibility notes: - borg init: removed the default of "--encryption/-e", #1979 This was done so users do a informed decision about -e mode. Bug fixes: - borg recreate: don't rechunkify unless explicitly told so - borg info: fixed bug when called without arguments, #1914 - borg init: fix free space check crashing if disk is full, #1821 - borg debug delete/get obj: fix wrong reference to exception - fix processing of remote ~/ and ~user/ paths (regressed since 1.1.0b1), #1759 - posix platform module: only build / import on non-win32 platforms, #2041 New features: - new CRC32 implementations that are much faster than the zlib one used previously, #1970 - add blake2b key modes (use blake2b as MAC). This links against system libb2, if possible, otherwise uses bundled code - automatically remove stale locks - set BORG_HOSTNAME_IS_UNIQUE env var to enable stale lock killing. If set, stale locks in both cache and repository are deleted. #562 #1253 - borg info : print general repo information, #1680 - borg check --first / --last / --sort / --prefix, #1663 - borg mount --first / --last / --sort / --prefix, #1542 - implement "health" item formatter key, #1749 - BORG_SECURITY_DIR to remember security related infos outside the cache. Key type, location and manifest timestamp checks now survive cache deletion. This also means that you can now delete your cache and avoid previous warnings, since Borg can still tell it's safe. - implement BORG_NEW_PASSPHRASE, #1768 Other changes: - borg recreate: - remove special-cased --dry-run - update --help - remove bloat: interruption blah, autocommit blah, resuming blah - re-use existing checkpoint functionality - archiver tests: add check_cache tool - lints refcounts - fixed cache sync performance regression from 1.1.0b1 onwards, #1940 - syncing the cache without chunks.archive.d now avoids any merges and is thus faster, #1940 - borg check --verify-data: faster due to linear on-disk-order scan - borg debug-xxx commands removed, we use "debug xxx" subcommands now, #1627 - improve metadata handling speed - shortcut hashindex_set by having hashindex_lookup hint about address - improve / add progress displays, #1721 - check for index vs. segment files object count mismatch - make RPC protocol more extensible: use named parameters. - RemoteRepository: misc. code cleanups / refactors - clarify cache/repository README file - docs: - quickstart: add a comment about other (remote) filesystems - quickstart: only give one possible ssh url syntax, all others are documented in usage chapter. - mention file:// - document repo URLs / archive location - clarify borg diff help, #980 - deployment: synthesize alternative --restrict-to-path example - improve cache / index docs, esp. files cache docs, #1825 - document using "git merge 1.0-maint -s recursive -X rename-threshold=20%" for avoiding troubles when merging the 1.0-maint branch into master. - tests: - FUSE tests: catch ENOTSUP on freebsd - FUSE tests: test troublesome xattrs last - fix byte range error in test, #1740 - use monkeypatch to set env vars, but only on pytest based tests. - point XDG_*_HOME to temp dirs for tests, #1714 - remove all BORG_* env vars from the outer environment Version 1.1.0b2 (2016-10-01) ---------------------------- Bug fixes: - fix incorrect preservation of delete tags, leading to "object count mismatch" on borg check, #1598. This only occurred with 1.1.0b1 (not with 1.0.x) and is normally fixed by running another borg create/delete/prune. - fix broken --progress for double-cell paths (e.g. CJK), #1624 - borg recreate: also catch SIGHUP - FUSE: - fix hardlinks in versions view, #1599 - add parameter check to ItemCache.get to make potential failures more clear New features: - Archiver, RemoteRepository: add --remote-ratelimit (send data) - borg help compression, #1582 - borg check: delete chunks with integrity errors, #1575, so they can be "repaired" immediately and maybe healed later. - archives filters concept (refactoring/unifying older code) - covers --first/--last/--prefix/--sort-by options - currently used for borg list/info/delete Other changes: - borg check --verify-data slightly tuned (use get_many()) - change {utcnow} and {now} to ISO-8601 format ("T" date/time separator) - repo check: log transaction IDs, improve object count mismatch diagnostic - Vagrantfile: use TW's fresh-bootloader pyinstaller branch - fix module names in api.rst - hashindex: bump api_version Version 1.1.0b1 (2016-08-28) ---------------------------- New features: - new commands: - borg recreate: re-create existing archives, #787 #686 #630 #70, also see #757, #770. - selectively remove files/dirs from old archives - re-compress data - re-chunkify data, e.g. to have upgraded Attic / Borg 0.xx archives deduplicate with Borg 1.x archives or to experiment with chunker-params. - borg diff: show differences between archives - borg with-lock: execute a command with the repository locked, #990 - borg create: - Flexible compression with pattern matching on path/filename, and LZ4 heuristic for deciding compressibility, #810, #1007 - visit files in inode order (better speed, esp. for large directories and rotating disks) - in-file checkpoints, #1217 - increased default checkpoint interval to 30 minutes (was 5 minutes), #896 - added uuid archive format tag, #1151 - save mountpoint directories with --one-file-system, makes system restore easier, #1033 - Linux: added support for some BSD flags, #1050 - add 'x' status for excluded paths, #814 - also means files excluded via UF_NODUMP, #1080 - borg check: - will not produce the "Checking segments" output unless new --progress option is passed, #824. - --verify-data to verify data cryptographically on the client, #975 - borg list, #751, #1179 - removed {formatkeys}, see "borg list --help" - --list-format is deprecated, use --format instead - --format now also applies to listing archives, not only archive contents, #1179 - now supports the usual [PATH [PATHS…]] syntax and excludes - new keys: csize, num_chunks, unique_chunks, NUL - supports guaranteed_available hashlib hashes (to avoid varying functionality depending on environment), which includes the SHA1 and SHA2 family as well as MD5 - borg prune: - to visualize the "thinning out" better, we now list all archives in reverse time order. rephrase and reorder help text. - implement --keep-last N via --keep-secondly N, also --keep-minutely. assuming that there is not more than 1 backup archive made in 1s, --keep-last N and --keep-secondly N are equivalent, #537 - cleanup checkpoints except the latest, #1008 - borg extract: - added --progress, #1449 - Linux: limited support for BSD flags, #1050 - borg info: - output is now more similar to borg create --stats, #977 - borg mount: - provide "borgfs" wrapper for borg mount, enables usage via fstab, #743 - "versions" mount option - when used with a repository mount, this gives a merged, versioned view of the files in all archives, #729 - repository: - added progress information to commit/compaction phase (often takes some time when deleting/pruning), #1519 - automatic recovery for some forms of repository inconsistency, #858 - check free space before going forward with a commit, #1336 - improved write performance (esp. for rotating media), #985 - new IO code for Linux - raised default segment size to approx 512 MiB - improved compaction performance, #1041 - reduced client CPU load and improved performance for remote repositories, #940 - options that imply output (--show-rc, --show-version, --list, --stats, --progress) don't need -v/--info to have that output displayed, #865 - add archive comments (via borg (re)create --comment), #842 - borg list/prune/delete: also output archive id, #731 - --show-version: shows/logs the borg version, #725 - added --debug-topic for granular debug logging, #1447 - use atomic file writing/updating for configuration and key files, #1377 - BORG_KEY_FILE environment variable, #1001 - self-testing module, #970 Bug fixes: - list: fixed default output being produced if --format is given with empty parameter, #1489 - create: fixed overflowing progress line with CJK and similar characters, #1051 - prune: fixed crash if --prefix resulted in no matches, #1029 - init: clean up partial repo if passphrase input is aborted, #850 - info: quote cmdline arguments that have spaces in them - fix hardlinks failing in some cases for extracting subtrees, #761 Other changes: - replace stdlib hmac with OpenSSL, zero-copy decrypt (10-15% increase in performance of hash-lists and extract). - improved chunker performance, #1021 - open repository segment files in exclusive mode (fail-safe), #1134 - improved error logging, #1440 - Source: - pass meta-data around, #765 - move some constants to new constants module - better readability and fewer errors with namedtuples, #823 - moved source tree into src/ subdirectory, #1016 - made borg.platform a package, #1113 - removed dead crypto code, #1032 - improved and ported parts of the test suite to py.test, #912 - created data classes instead of passing dictionaries around, #981, #1158, #1161 - cleaned up imports, #1112 - Docs: - better help texts and sphinx reproduction of usage help: - Group options - Nicer list of options in Sphinx - Deduplicate 'Common options' (including --help) - chunker: added some insights by "Voltara", #903 - clarify what "deduplicated size" means - fix / update / add package list entries - added a SaltStack usage example, #956 - expanded FAQ - new contributors in AUTHORS! - Tests: - vagrant: add ubuntu/xenial 64bit - this box has still some issues - ChunkBuffer: add test for leaving partial chunk in buffer, fixes #945 Version 1.0.13 (2019-02-15) --------------------------- Please note: this is very likely the last 1.0.x release, please upgrade to 1.1.x. Bug fixes: - security fix: configure FUSE with "default_permissions", #3903. "default_permissions" is now enforced by borg by default to let the kernel check uid/gid/mode based permissions. "ignore_permissions" can be given not to enforce "default_permissions". - xattrs: fix borg exception handling on ENOSPC error, #3808. New features: - Read a passphrase from a file descriptor specified in the BORG_PASSPHRASE_FD environment variable. Other changes: - acl platform code: fix acl set return type - xattr: - add linux {list,get,set}xattr ctypes prototypes - fix darwin flistxattr ctypes prototype - testing / travis-ci: - fix the homebrew 1.9 issues on travis-ci, #4254 - travis OS X: use xcode 8.3 (not broken) - tox.ini: lock requirements - unbreak 1.0-maint on travis, fixes #4123 - vagrant: - misc. fixes - FUSE for macOS: upgrade 3.7.1 to 3.8.3 - Python: upgrade 3.5.5 to 3.5.6 - docs: - Update installation instructions for macOS - update release workflow using twine (docs, scripts), #4213 Version 1.0.12 (2018-04-08) --------------------------- Bug fixes: - repository: cleanup/write: invalidate cached FDs, tests - serve: fix exitcode, #2910 - extract: set bsdflags last (include immutable flag), #3263 - create --timestamp: set start time, #2957 - create: show excluded dir with "x" for tagged dirs / caches, #3189 - migrate locks to child PID when daemonize is used - Buffer: fix wrong thread-local storage use, #2951 - fix detection of non-local path, #3108 - fix LDLP restoration for subprocesses, #3077 - fix subprocess environments (xattr module's fakeroot version check, borg umount, BORG_PASSCOMMAND), #3050 - remote: deal with partial lines, #2637 - get rid of datetime.isoformat, use safe parse_timestamp to parse timestamps, #2994 - build: do .h file content checks in binary mode, fixes build issue for non-ascii header files on pure-ascii locale platforms, #3544 #3639 - remove platform.uname() call which caused library mismatch issues, #3732 - add exception handler around deprecated platform.linux_distribution() call Other changes: - require msgpack-python >= 0.4.6 and < 0.5.0, see #3753 - add parens for C preprocessor macro argument usages (did not cause malfunction) - ignore corrupt files cache, #2939 - replace "modulo" with "if" to check for wraparound in hashmap - keymanager: don't depend on optional readline module, #2980 - exclude broken pytest 3.3.0 release - exclude broken Cython 0.27(.0) release, #3066 - flake8: add some ignores - docs: - create: document exclusion through nodump - document good and problematic option placements, fix examples, #3356 - update docs about hardlinked symlinks limitation - faq: we do not implement futile attempts of ETA / progress displays - simplified rate limiting wrapper in FAQ - twitter account @borgbackup, #2948 - add note about metadata dedup and --no[ac]time, #2518 - change-passphrase only changes the passphrase, #2990 - clarify encrypted key format for borg key export, #3296 - document sshfs rename workaround, #3315 - update release checklist about security fixes - docs about how to verify a signed release, #3634 - chunk seed is generated per /repository/ - vagrant: - use FUSE for macOS 3.7.1 to build the macOS binary - use python 3.5.5 to build the binaries - add exe location to PATH when we build an exe - use https pypi url for wheezy - netbsd: bash is already installed - netbsd: fix netbsd version in PKG_PATH - use self-made FreeBSD 10.3 box, #3022 - backport fs_init (including related updates) from 1.1 - the boxcutter wheezy boxes are 404, use local ones - travis: - don't perform full Travis build on docs-only changes, #2531 - only short-circuit docs-only changes for pull requests Version 1.0.11 (2017-07-21) --------------------------- Bug fixes: - use limited unpacker for outer key (security precaution), #2174 - fix paperkey import bug Other changes: - change --checkpoint-interval default from 600s to 1800s, #2841. this improves efficiency for big repositories a lot. - docs: fix OpenSUSE command and add OpenSUSE section - tests: add tests for split_lstring and paperkey - vagrant: - fix openbsd shell - backport cpu/ram setup from master - add stretch64 VM Version 1.0.11rc1 (2017-06-27) ------------------------------ Bug fixes: - performance: rebuild hashtable if we have too few empty buckets, #2246. this fixes some sporadic, but severe performance breakdowns. - Archive: allocate zeros when needed, #2308 fixes huge memory usage of mount (8 MiB × number of archives) - IPv6 address support also: Location: more informative exception when parsing fails - borg single-file binary: use pyinstaller v3.2.1, #2396 this fixes that the prelink cronjob on some distros kills the borg binary by stripping away parts of it. - extract: - warning for unextracted big extended attributes, #2258 - also create parent dir for device files, if needed. - don't write to disk with --stdout, #2645 - archive check: detect and fix missing all-zero replacement chunks, #2180 - fix (de)compression exceptions, #2224 #2221 - files cache: update inode number, #2226 - borg rpc: use limited msgpack.Unpacker (security precaution), #2139 - Manifest: use limited msgpack.Unpacker (security precaution), #2175 - Location: accept //servername/share/path - fix ChunkIndex.__contains__ assertion for big-endian archs (harmless) - create: handle BackupOSError on a per-path level in one spot - fix error msg, there is no --keep-last in borg 1.0.x, #2282 - clamp (nano)second values to unproblematic range, #2304 - fuse / borg mount: - fix st_blocks to be an integer (not float) value - fix negative uid/gid crash (they could come into archives e.g. when backing up external drives under cygwin), #2674 - fix crash if empty (None) xattr is read - do pre-mount checks before opening repository - check llfuse is installed before asking for passphrase - borg rename: expand placeholders, #2386 - borg serve: fix forced command lines containing BORG_* env vars - fix error msg, it is --keep-within, not --within - fix borg key/debug/benchmark crashing without subcommand, #2240 - chunker: fix invalid use of types, don't do uint32_t >> 32 - document follow_symlinks requirements, check libc, #2507 New features: - added BORG_PASSCOMMAND environment variable, #2573 - add minimal version of in repository mandatory feature flags, #2134 This should allow us to make sure older borg versions can be cleanly prevented from doing operations that are no longer safe because of repository format evolution. This allows more fine grained control than just incrementing the manifest version. So for example a change that still allows new archives to be created but would corrupt the repository when an old version tries to delete an archive or check the repository would add the new feature to the check and delete set but leave it out of the write set. - borg delete --force --force to delete severely corrupted archives, #1975 Other changes: - embrace y2038 issue to support 32bit platforms - be more clear that this is a "beyond repair" case, #2427 - key file names: limit to 100 characters and remove colons from host name - upgrade FUSE for macOS to 3.5.8, #2346 - split up parsing and filtering for --keep-within, better error message, #2610 - docs: - fix caskroom link, #2299 - address SSH batch mode, #2202 #2270 - improve remote-path description - document snapshot usage, #2178 - document relative path usage, #1868 - one link per distro in the installation page - development: new branching model in git repository - kill api page - added FAQ section about backing up root partition - add bountysource badge, #2558 - create empty docs.txt requirements, #2694 - README: how to help the project - note -v/--verbose requirement on affected options, #2542 - document borg init behaviour via append-only borg serve, #2440 - be clear about what buzhash is used for (chunking) and want it is not used for (deduplication)- also say already in the readme that we use a cryptohash for dedupe, so people don't worry, #2390 - add hint about chunker params to borg upgrade docs, #2421 - clarify borg upgrade docs, #2436 - quickstart: delete problematic BORG_PASSPHRASE use, #2623 - faq: specify "using inline shell scripts" - document pattern denial of service, #2624 - tests: - remove attic dependency of the tests, #2505 - travis: - enhance travis setuptools_scm situation - install fakeroot for Linux - add test for borg delete --force - enable remote tests on cygwin (the cygwin issue that caused these tests to break was fixed in cygwin at least since cygwin 2.8, maybe even since 2.7.0). - remove skipping the noatime tests on GNU/Hurd, #2710 - fix borg import issue, add comment, #2718 - include attic.tar.gz when installing the package also: add include_package_data=True Version 1.0.10 (2017-02-13) --------------------------- Bug fixes: - Manifest timestamps are now monotonically increasing, this fixes issues when the system clock jumps backwards or is set inconsistently across computers accessing the same repository, #2115 - Fixed testing regression in 1.0.10rc1 that lead to a hard dependency on py.test >= 3.0, #2112 New features: - "key export" can now generate a printable HTML page with both a QR code and a human-readable "paperkey" representation (and custom text) through the ``--qr-html`` option. The same functionality is also available through `paperkey.html `_, which is the same HTML page generated by ``--qr-html``. It works with existing "key export" files and key files. Other changes: - docs: - language clarification - "borg create --one-file-system" option does not respect mount points, but considers different file systems instead, #2141 - setup.py: build_api: sort file list for determinism Version 1.0.10rc1 (2017-01-29) ------------------------------ Bug fixes: - borg serve: fix transmission data loss of pipe writes, #1268 This affects only the cygwin platform (not Linux, BSD, OS X). - Avoid triggering an ObjectiveFS bug in xattr retrieval, #1992 - When running out of buffer memory when reading xattrs, only skip the current file, #1993 - Fixed "borg upgrade --tam" crashing with unencrypted repositories. Since :ref:`the issue ` is not relevant for unencrypted repositories, it now does nothing and prints an error, #1981. - Fixed change-passphrase crashing with unencrypted repositories, #1978 - Fixed "borg check repo::archive" indicating success if "archive" does not exist, #1997 - borg check: print non-exit-code warning if --last or --prefix aren't fulfilled - fix bad parsing of wrong repo location syntax - create: don't create hard link refs to failed files, mount: handle invalid hard link refs, #2092 - detect mingw byte order, #2073 - creating a new segment: use "xb" mode, #2099 - mount: umount on SIGINT/^C when in foreground, #2082 Other changes: - binary: use fixed AND freshly compiled pyinstaller bootloader, #2002 - xattr: ignore empty names returned by llistxattr(2) et al - Enable the fault handler: install handlers for the SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals to dump the Python traceback. - Also print a traceback on SIGUSR2. - borg change-passphrase: print key location (simplify making a backup of it) - officially support Python 3.6 (setup.py: add Python 3.6 qualifier) - tests: - vagrant / travis / tox: add Python 3.6 based testing - vagrant: fix openbsd repo, #2042 - vagrant: fix the freebsd64 machine, #2037 #2067 - vagrant: use python 3.5.3 to build binaries, #2078 - vagrant: use osxfuse 3.5.4 for tests / to build binaries vagrant: improve darwin64 VM settings - travis: fix osxfuse install (fixes OS X testing on Travis CI) - travis: require succeeding OS X tests, #2028 - travis: use latest pythons for OS X based testing - use pytest-xdist to parallelize testing - fix xattr test race condition, #2047 - setup.cfg: fix pytest deprecation warning, #2050 - docs: - language clarification - VM backup FAQ - borg create: document how to back up stdin, #2013 - borg upgrade: fix incorrect title levels - add CVE numbers for issues fixed in 1.0.9, #2106 - fix typos (taken from Debian package patch) - remote: include data hexdump in "unexpected RPC data" error message - remote: log SSH command line at debug level - API_VERSION: use numberspaces, #2023 - remove .github from pypi package, #2051 - add pip and setuptools to requirements file, #2030 - SyncFile: fix use of fd object after close (cosmetic) - Manifest.in: simplify, exclude \*.{so,dll,orig}, #2066 - ignore posix_fadvise errors in repository.py, #2095 (works around issues with docker on ARM) - make LoggedIO.close_segment reentrant, avoid reentrance Version 1.0.9 (2016-12-20) -------------------------- Security fixes: - A flaw in the cryptographic authentication scheme in Borg allowed an attacker to spoof the manifest. See :ref:`tam_vuln` above for the steps you should take. CVE-2016-10099 was assigned to this vulnerability. - borg check: When rebuilding the manifest (which should only be needed very rarely) duplicate archive names would be handled on a "first come first serve" basis, potentially opening an attack vector to replace archives. Example: were there 2 archives named "foo" in a repo (which can not happen under normal circumstances, because borg checks if the name is already used) and a "borg check" recreated a (previously lost) manifest, the first of the archives it encountered would be in the manifest. The second archive is also still in the repo, but not referenced in the manifest, in this case. If the second archive is the "correct" one (and was previously referenced from the manifest), it looks like it got replaced by the first one. In the manifest, it actually got replaced. Both remain in the repo but the "correct" one is no longer accessible via normal means - the manifest. CVE-2016-10100 was assigned to this vulnerability. Bug fixes: - borg check: - rebuild manifest if it's corrupted - skip corrupted chunks during manifest rebuild - fix TypeError in integrity error handler, #1903, #1894 - fix location parser for archives with @ char (regression introduced in 1.0.8), #1930 - fix wrong duration/timestamps if system clock jumped during a create - fix progress display not updating if system clock jumps backwards - fix checkpoint interval being incorrect if system clock jumps Other changes: - docs: - add python3-devel as a dependency for cygwin-based installation - clarify extract is relative to current directory - FAQ: fix link to changelog - markup fixes - tests: - test_get\_(cache|keys)_dir: clean env state, #1897 - get back pytest's pretty assertion failures, #1938 - setup.py build_usage: - fixed build_usage not processing all commands - fixed build_usage not generating includes for debug commands Version 1.0.9rc1 (2016-11-27) ----------------------------- Bug fixes: - files cache: fix determination of newest mtime in backup set (which is used in cache cleanup and led to wrong "A" [added] status for unchanged files in next backup), #1860. - borg check: - fix incorrectly reporting attic 0.13 and earlier archives as corrupt - handle repo w/o objects gracefully and also bail out early if repo is *completely* empty, #1815. - fix tox/pybuild in 1.0-maint - at xattr module import time, loggers are not initialized yet New features: - borg umount exposed already existing umount code via the CLI api, so users can use it, which is more consistent than using borg to mount and fusermount -u (or umount) to un-mount, #1855. - implement borg create --noatime --noctime, fixes #1853 Other changes: - docs: - display README correctly on PyPI - improve cache / index docs, esp. files cache docs, fixes #1825 - different pattern matching for --exclude, #1779 - datetime formatting examples for {now} placeholder, #1822 - clarify passphrase mode attic repo upgrade, #1854 - clarify --umask usage, #1859 - clarify how to choose PR target branch - clarify prune behavior for different archive contents, #1824 - fix PDF issues, add logo, fix authors, headings, TOC - move security verification to support section - fix links in standalone README (:ref: tags) - add link to security contact in README - add FAQ about security - move fork differences to FAQ - add more details about resource usage - tests: skip remote tests on cygwin, #1268 - travis: - allow OS X failures until the brew cask osxfuse issue is fixed - caskroom osxfuse-beta gone, it's osxfuse now (3.5.3) - vagrant: - upgrade OSXfuse / FUSE for macOS to 3.5.3 - remove llfuse from tox.ini at a central place - do not try to install llfuse on centos6 - fix FUSE test for darwin, #1546 - add windows virtual machine with cygwin - Vagrantfile cleanup / code deduplication Version 1.0.8 (2016-10-29) -------------------------- Bug fixes: - RemoteRepository: Fix busy wait in call_many, #940 New features: - implement borgmajor/borgminor/borgpatch placeholders, #1694 {borgversion} was already there (full version string). With the new placeholders you can now also get e.g. 1 or 1.0 or 1.0.8. Other changes: - avoid previous_location mismatch, #1741 due to the changed canonicalization for relative paths in PR #1711 / #1655 (implement /./ relpath hack), there would be a changed repo location warning and the user would be asked if this is ok. this would break automation and require manual intervention, which is unwanted. thus, we automatically fix the previous_location config entry, if it only changed in the expected way, but still means the same location. - docs: - deployment.rst: do not use bare variables in ansible snippet - add clarification about append-only mode, #1689 - setup.py: add comment about requiring llfuse, #1726 - update usage.rst / api.rst - repo url / archive location docs + typo fix - quickstart: add a comment about other (remote) filesystems - vagrant / tests: - no chown when rsyncing (fixes boxes w/o vagrant group) - fix FUSE permission issues on linux/freebsd, #1544 - skip FUSE test for borg binary + fakeroot - ignore security.selinux xattrs, fixes tests on centos, #1735 Version 1.0.8rc1 (2016-10-17) ----------------------------- Bug fixes: - fix signal handling (SIGINT, SIGTERM, SIGHUP), #1620 #1593 Fixes e.g. leftover lock files for quickly repeated signals (e.g. Ctrl-C Ctrl-C) or lost connections or systemd sending SIGHUP. - progress display: adapt formatting to narrow screens, do not crash, #1628 - borg create --read-special - fix crash on broken symlink, #1584. also correctly processes broken symlinks. before this regressed to a crash (5b45385) a broken symlink would've been skipped. - process_symlink: fix missing backup_io() Fixes a chmod/chown/chgrp/unlink/rename/... crash race between getting dirents and dispatching to process_symlink. - yes(): abort on wrong answers, saying so, #1622 - fixed exception borg serve raised when connection was closed before repository was opened. Add an error message for this. - fix read-from-closed-FD issue, #1551 (this seems not to get triggered in 1.0.x, but was discovered in master) - hashindex: fix iterators (always raise StopIteration when exhausted) (this seems not to get triggered in 1.0.x, but was discovered in master) - enable relative paths in ssh:// repo URLs, via /./relpath hack, #1655 - allow repo paths with colons, #1705 - update changed repo location immediately after acceptance, #1524 - fix debug get-obj / delete-obj crash if object not found and remote repo, #1684 - pyinstaller: use a spec file to build borg.exe binary, exclude osxfuse dylib on Mac OS X (avoids mismatch lib <-> driver), #1619 New features: - add "borg key export" / "borg key import" commands, #1555, so users are able to back up / restore their encryption keys more easily. Supported formats are the keyfile format used by borg internally and a special "paper" format with by line checksums for printed backups. For the paper format, the import is an interactive process which checks each line as soon as it is input. - add "borg debug-refcount-obj" to determine a repo objects' referrer counts, #1352 Other changes: - add "borg debug ..." subcommands (borg debug-* still works, but will be removed in borg 1.1) - setup.py: Add subcommand support to build_usage. - remote: change exception message for unexpected RPC data format to indicate dataflow direction. - improved messages / error reporting: - IntegrityError: add placeholder for message, so that the message we give appears not only in the traceback, but also in the (short) error message, #1572 - borg.key: include chunk id in exception msgs, #1571 - better messages for cache newer than repo, #1700 - vagrant (testing/build VMs): - upgrade OSXfuse / FUSE for macOS to 3.5.2 - update Debian Wheezy boxes, #1686 - openbsd / netbsd: use own boxes, fixes misc rsync installation and FUSE/llfuse related testing issues, #1695 #1696 #1670 #1671 #1728 - docs: - add docs for "key export" and "key import" commands, #1641 - fix inconsistency in FAQ (pv-wrapper). - fix second block in "Easy to use" section not showing on GitHub, #1576 - add bestpractices badge - link reference docs and faq about BORG_FILES_CACHE_TTL, #1561 - improve borg info --help, explain size infos, #1532 - add release signing key / security contact to README, #1560 - add contribution guidelines for developers - development.rst: add sphinx_rtd_theme to the sphinx install command - adjust border color in borg.css - add debug-info usage help file - internals.rst: fix typos - setup.py: fix build_usage to always process all commands - added docs explaining multiple --restrict-to-path flags, #1602 - add more specific warning about write-access debug commands, #1587 - clarify FAQ regarding backup of virtual machines, #1672 - tests: - work around FUSE xattr test issue with recent fakeroot - simplify repo/hashindex tests - travis: test FUSE-enabled borg, use trusty to have a recent FUSE - re-enable FUSE tests for RemoteArchiver (no deadlocks any more) - clean env for pytest based tests, #1714 - fuse_mount contextmanager: accept any options Version 1.0.7 (2016-08-19) -------------------------- Security fixes: - borg serve: fix security issue with remote repository access, #1428 If you used e.g. --restrict-to-path /path/client1/ (with or without trailing slash does not make a difference), it acted like a path prefix match using /path/client1 (note the missing trailing slash) - the code then also allowed working in e.g. /path/client13 or /path/client1000. As this could accidentally lead to major security/privacy issues depending on the paths you use, the behaviour was changed to be a strict directory match. That means --restrict-to-path /path/client1 (with or without trailing slash does not make a difference) now uses /path/client1/ internally (note the trailing slash here!) for matching and allows precisely that path AND any path below it. So, /path/client1 is allowed, /path/client1/repo1 is allowed, but not /path/client13 or /path/client1000. If you willingly used the undocumented (dangerous) previous behaviour, you may need to rearrange your --restrict-to-path paths now. We are sorry if that causes work for you, but we did not want a potentially dangerous behaviour in the software (not even using a for-backwards-compat option). Bug fixes: - fixed repeated LockTimeout exceptions when borg serve tried to write into a already write-locked repo (e.g. by a borg mount), #502 part b) This was solved by the fix for #1220 in 1.0.7rc1 already. - fix cosmetics + file leftover for "not a valid borg repository", #1490 - Cache: release lock if cache is invalid, #1501 - borg extract --strip-components: fix leak of preloaded chunk contents - Repository, when a InvalidRepository exception happens: - fix spurious, empty lock.roster - fix repo not closed cleanly New features: - implement borg debug-info, fixes #1122 (just calls already existing code via cli, same output as below tracebacks) Other changes: - skip the O_NOATIME test on GNU Hurd, fixes #1315 (this is a very minor issue and the GNU Hurd project knows the bug) - document using a clean repo to test / build the release Version 1.0.7rc2 (2016-08-13) ----------------------------- Bug fixes: - do not write objects to repository that are bigger than the allowed size, borg will reject reading them, #1451. Important: if you created archives with many millions of files or directories, please verify if you can open them successfully, e.g. try a "borg list REPO::ARCHIVE". - lz4 compression: dynamically enlarge the (de)compression buffer, the static buffer was not big enough for archives with extremely many items, #1453 - larger item metadata stream chunks, raise archive item limit by 8x, #1452 - fix untracked segments made by moved DELETEs, #1442 Impact: Previously (metadata) segments could become untracked when deleting data, these would never be cleaned up. - extended attributes (xattrs) related fixes: - fixed a race condition in xattrs querying that led to the entire file not being backed up (while logging the error, exit code = 1), #1469 - fixed a race condition in xattrs querying that led to a crash, #1462 - raise OSError including the error message derived from errno, deal with path being a integer FD Other changes: - print active env var override by default, #1467 - xattr module: refactor code, deduplicate, clean up - repository: split object size check into too small and too big - add a transaction_id assertion, so borg init on a broken (inconsistent) filesystem does not look like a coding error in borg, but points to the real problem. - explain confusing TypeError caused by compat support for old servers, #1456 - add forgotten usage help file from build_usage - refactor/unify buffer code into helpers.Buffer class, add tests - docs: - document archive limitation, #1452 - improve prune examples Version 1.0.7rc1 (2016-08-05) ----------------------------- Bug fixes: - fix repo lock deadlocks (related to lock upgrade), #1220 - catch unpacker exceptions, resync, #1351 - fix borg break-lock ignoring BORG_REPO env var, #1324 - files cache performance fixes (fixes unnecessary re-reading/chunking/ hashing of unmodified files for some use cases): - fix unintended file cache eviction, #1430 - implement BORG_FILES_CACHE_TTL, update FAQ, raise default TTL from 10 to 20, #1338 - FUSE: - cache partially read data chunks (performance), #965, #966 - always create a root dir, #1125 - use an OrderedDict for helptext, making the build reproducible, #1346 - RemoteRepository init: always call close on exceptions, #1370 (cosmetic) - ignore stdout/stderr broken pipe errors (cosmetic), #1116 New features: - better borg versions management support (useful esp. for borg servers wanting to offer multiple borg versions and for clients wanting to choose a specific server borg version), #1392: - add BORG_VERSION environment variable before executing "borg serve" via ssh - add new placeholder {borgversion} - substitute placeholders in --remote-path - borg init --append-only option (makes using the more secure append-only mode more convenient. when used remotely, this requires 1.0.7+ also on the borg server), #1291. Other changes: - Vagrantfile: - darwin64: upgrade to FUSE for macOS 3.4.1 (aka osxfuse), #1378 - xenial64: use user "ubuntu", not "vagrant" (as usual), #1331 - tests: - fix FUSE tests on OS X, #1433 - docs: - FAQ: add backup using stable filesystem names recommendation - FAQ about glibc compatibility added, #491, glibc-check improved - FAQ: 'A' unchanged file; remove ambiguous entry age sentence. - OS X: install pkg-config to build with FUSE support, fixes #1400 - add notes about shell/sudo pitfalls with env. vars, #1380 - added platform feature matrix - implement borg debug-dump-repo-objs Version 1.0.6 (2016-07-12) -------------------------- Bug fixes: - Linux: handle multiple LD_PRELOAD entries correctly, #1314, #1111 - Fix crash with unclear message if the libc is not found, #1314, #1111 Other changes: - tests: - Fixed O_NOATIME tests for Solaris and GNU Hurd, #1315 - Fixed sparse file tests for (file) systems not supporting it, #1310 - docs: - Fixed syntax highlighting, #1313 - misc docs: added data processing overview picture Version 1.0.6rc1 (2016-07-10) ----------------------------- New features: - borg check --repair: heal damaged files if missing chunks re-appear (e.g. if the previously missing chunk was added again in a later backup archive), #148. (*) Also improved logging. Bug fixes: - sync_dir: silence fsync() failing with EINVAL, #1287 Some network filesystems (like smbfs) don't support this and we use this in repository code. - borg mount (FUSE): - fix directories being shadowed when contained paths were also specified, #1295 - raise I/O Error (EIO) on damaged files (unless -o allow_damaged_files is used), #1302. (*) - borg extract: warn if a damaged file is extracted, #1299. (*) - Added some missing return code checks (ChunkIndex._add, hashindex_resize). - borg check: fix/optimize initial hash table size, avoids resize of the table. Other changes: - tests: - add more FUSE tests, #1284 - deduplicate FUSE (u)mount code - fix borg binary test issues, #862 - docs: - changelog: added release dates to older borg releases - fix some sphinx (docs generator) warnings, #881 Notes: (*) Some features depend on information (chunks_healthy list) added to item metadata when a file with missing chunks was "repaired" using all-zero replacement chunks. The chunks_healthy list is generated since borg 1.0.4, thus borg can't recognize such "repaired" (but content-damaged) files if the repair was done with an older borg version. Version 1.0.5 (2016-07-07) -------------------------- Bug fixes: - borg mount: fix FUSE crash in xattr code on Linux introduced in 1.0.4, #1282 Other changes: - backport some FAQ entries from master branch - add release helper scripts - Vagrantfile: - centos6: no FUSE, don't build binary - add xz for redhat-like dists Version 1.0.4 (2016-07-07) -------------------------- New features: - borg serve --append-only, #1168 This was included because it was a simple change (append-only functionality was already present via repository config file) and makes better security now practically usable. - BORG_REMOTE_PATH environment variable, #1258 This was included because it was a simple change (--remote-path cli option was already present) and makes borg much easier to use if you need it. - Repository: cleanup incomplete transaction on "no space left" condition. In many cases, this can avoid a 100% full repo filesystem (which is very problematic as borg always needs free space - even to delete archives). Bug fixes: - Fix wrong handling and reporting of OSErrors in borg create, #1138. This was a serious issue: in the context of "borg create", errors like repository I/O errors (e.g. disk I/O errors, ssh repo connection errors) were handled badly and did not lead to a crash (which would be good for this case, because the repo transaction would be incomplete and trigger a transaction rollback to clean up). Now, error handling for source files is cleanly separated from every other error handling, so only problematic input files are logged and skipped. - Implement fail-safe error handling for borg extract. Note that this isn't nearly as critical as the borg create error handling bug, since nothing is written to the repo. So this was "merely" misleading error reporting. - Add missing error handler in directory attr restore loop. - repo: make sure write data hits disk before the commit tag (#1236) and also sync the containing directory. - FUSE: getxattr fail must use errno.ENOATTR, #1126 (fixes Mac OS X Finder malfunction: "zero bytes" file length, access denied) - borg check --repair: do not lose information about the good/original chunks. If we do not lose the original chunk IDs list when "repairing" a file (replacing missing chunks with all-zero chunks), we have a chance to "heal" the file back into its original state later, in case the chunks re-appear (e.g. in a fresh backup). Healing is not implemented yet, see #148. - fixes for --read-special mode: - ignore known files cache, #1241 - fake regular file mode, #1214 - improve symlinks handling, #1215 - remove passphrase from subprocess environment, #1105 - Ignore empty index file (will trigger index rebuild), #1195 - add missing placeholder support for --prefix, #1027 - improve exception handling for placeholder replacement - catch and format exceptions in arg parsing - helpers: fix "undefined name 'e'" in exception handler - better error handling for missing repo manifest, #1043 - borg delete: - make it possible to delete a repo without manifest - borg delete --forced allows one to delete corrupted archives, #1139 - borg check: - make borg check work for empty repo - fix resync and msgpacked item qualifier, #1135 - rebuild_manifest: fix crash if 'name' or 'time' key were missing. - better validation of item metadata dicts, #1130 - better validation of archive metadata dicts - close the repo on exit - even if rollback did not work, #1197. This is rather cosmetic, it avoids repo closing in the destructor. - tests: - fix sparse file test, #1170 - flake8: ignore new F405, #1185 - catch "invalid argument" on cygwin, #257 - fix sparseness assertion in test prep, #1264 Other changes: - make borg build/work on OpenSSL 1.0 and 1.1, #1187 - docs / help: - fix / clarify prune help, #1143 - fix "patterns" help formatting - add missing docs / help about placeholders - resources: rename atticmatic to borgmatic - document sshd settings, #545 - more details about checkpoints, add split trick, #1171 - support docs: add freenode web chat link, #1175 - add prune visualization / example, #723 - add note that Fnmatch is default, #1247 - make clear that lzma levels > 6 are a waste of cpu cycles - add a "do not edit" note to auto-generated files, #1250 - update cygwin installation docs - repository interoperability with borg master (1.1dev) branch: - borg check: read item metadata keys from manifest, #1147 - read v2 hints files, #1235 - fix hints file "unknown version" error handling bug - tests: add tests for format_line - llfuse: update version requirement for freebsd - Vagrantfile: - use openbsd 5.9, #716 - do not install llfuse on netbsd (broken) - update OSXfuse to version 3.3.3 - use Python 3.5.2 to build the binaries - glibc compatibility checker: scripts/glibc_check.py - add .eggs to .gitignore Version 1.0.3 (2016-05-20) -------------------------- Bug fixes: - prune: avoid that checkpoints are kept and completed archives are deleted in a prune run), #997 - prune: fix commandline argument validation - some valid command lines were considered invalid (annoying, but harmless), #942 - fix capabilities extraction on Linux (set xattrs last, after chown()), #1069 - repository: fix commit tags being seen in data - when probing key files, do binary reads. avoids crash when non-borg binary files are located in borg's key files directory. - handle SIGTERM and make a clean exit - avoids orphan lock files. - repository cache: don't cache large objects (avoid using lots of temp. disk space), #1063 Other changes: - Vagrantfile: OS X: update osxfuse / install lzma package, #933 - setup.py: add check for platform_darwin.c - setup.py: on freebsd, use a llfuse release that builds ok - docs / help: - update readthedocs URLs, #991 - add missing docs for "borg break-lock", #992 - borg create help: add some words to about the archive name - borg create help: document format tags, #894 Version 1.0.2 (2016-04-16) -------------------------- Bug fixes: - fix malfunction and potential corruption on (nowadays rather rare) big-endian architectures or bi-endian archs in (rare) BE mode. #886, #889 cache resync / index merge was malfunctioning due to this, potentially leading to data loss. borg info had cosmetic issues (displayed wrong values). note: all (widespread) little-endian archs (like x86/x64) or bi-endian archs in (widespread) LE mode (like ARMEL, MIPSEL, ...) were NOT affected. - add overflow and range checks for 1st (special) uint32 of the hashindex values, switch from int32 to uint32. - fix so that refcount will never overflow, but just stick to max. value after a overflow would have occurred. - borg delete: fix --cache-only for broken caches, #874 Makes --cache-only idempotent: it won't fail if the cache is already deleted. - fixed borg create --one-file-system erroneously traversing into other filesystems (if starting fs device number was 0), #873 - workaround a bug in Linux fadvise FADV_DONTNEED, #907 Other changes: - better test coverage for hashindex, incl. overflow testing, checking correct computations so endianness issues would be discovered. - reproducible doc for ProgressIndicator*, make the build reproducible. - use latest llfuse for vagrant machines - docs: - use /path/to/repo in examples, fixes #901 - fix confusing usage of "repo" as archive name (use "arch") Version 1.0.1 (2016-04-08) -------------------------- New features: Usually there are no new features in a bugfix release, but these were added due to their high impact on security/safety/speed or because they are fixes also: - append-only mode for repositories, #809, #36 (see docs) - borg create: add --ignore-inode option to make borg detect unmodified files even if your filesystem does not have stable inode numbers (like sshfs and possibly CIFS). - add options --warning, --error, --critical for missing log levels, #826. it's not recommended to suppress warnings or errors, but the user may decide this on his own. note: --warning is not given to borg serve so a <= 1.0.0 borg will still work as server (it is not needed as it is the default). do not use --error or --critical when using a <= 1.0.0 borg server. Bug fixes: - fix silently skipping EIO, #748 - add context manager for Repository (avoid orphan repository locks), #285 - do not sleep for >60s while waiting for lock, #773 - unpack file stats before passing to FUSE - fix build on illumos - don't try to back up doors or event ports (Solaris and derivatives) - remove useless/misleading libc version display, #738 - test suite: reset exit code of persistent archiver, #844 - RemoteRepository: clean up pipe if remote open() fails - Remote: don't print tracebacks for Error exceptions handled downstream, #792 - if BORG_PASSPHRASE is present but wrong, don't prompt for password, but fail instead, #791 - ArchiveChecker: move "orphaned objects check skipped" to INFO log level, #826 - fix capitalization, add ellipses, change log level to debug for 2 messages, #798 Other changes: - update llfuse requirement, llfuse 1.0 works - update OS / dist packages on build machines, #717 - prefer showing --info over -v in usage help, #859 - docs: - fix cygwin requirements (gcc-g++) - document how to debug / file filesystem issues, #664 - fix reproducible build of api docs - RTD theme: CSS !important overwrite, #727 - Document logo font. Recreate logo png. Remove GIMP logo file. Version 1.0.0 (2016-03-05) -------------------------- The major release number change (0.x -> 1.x) indicates bigger incompatible changes, please read the compatibility notes, adapt / test your scripts and check your backup logs. Compatibility notes: - drop support for python 3.2 and 3.3, require 3.4 or 3.5, #221 #65 #490 note: we provide binaries that include python 3.5.1 and everything else needed. they are an option in case you are stuck with < 3.4 otherwise. - change encryption to be on by default (using "repokey" mode) - moved keyfile keys from ~/.borg/keys to ~/.config/borg/keys, you can either move them manually or run "borg upgrade " - remove support for --encryption=passphrase, use borg migrate-to-repokey to switch to repokey mode, #97 - remove deprecated --compression , use --compression zlib, instead in case of 0, you could also use --compression none - remove deprecated --hourly/daily/weekly/monthly/yearly use --keep-hourly/daily/weekly/monthly/yearly instead - remove deprecated --do-not-cross-mountpoints, use --one-file-system instead - disambiguate -p option, #563: - -p now is same as --progress - -P now is same as --prefix - remove deprecated "borg verify", use "borg extract --dry-run" instead - cleanup environment variable semantics, #355 the environment variables used to be "yes sayers" when set, this was conceptually generalized to "automatic answerers" and they just give their value as answer (as if you typed in that value when being asked). See the "usage" / "Environment Variables" section of the docs for details. - change the builtin default for --chunker-params, create 2MiB chunks, #343 --chunker-params new default: 19,23,21,4095 - old default: 10,23,16,4095 one of the biggest issues with borg < 1.0 (and also attic) was that it had a default target chunk size of 64kiB, thus it created a lot of chunks and thus also a huge chunk management overhead (high RAM and disk usage). please note that the new default won't change the chunks that you already have in your repository. the new big chunks do not deduplicate with the old small chunks, so expect your repo to grow at least by the size of every changed file and in the worst case (e.g. if your files cache was lost / is not used) by the size of every file (minus any compression you might use). in case you want to see a much lower resource usage immediately (RAM / disk) for chunks management, it might be better to start with a new repo than to continue in the existing repo (with an existing repo, you have to wait until all archives with small chunks get pruned to see a lower resource usage). if you used the old --chunker-params default value (or if you did not use --chunker-params option at all) and you'd like to continue using small chunks (and you accept the huge resource usage that comes with that), just use explicitly borg create --chunker-params=10,23,16,4095. - archive timestamps: the 'time' timestamp now refers to archive creation start time (was: end time), the new 'time_end' timestamp refers to archive creation end time. This might affect prune if your backups take a long time. if you give a timestamp via cli, this is stored into 'time'. therefore it now needs to mean archive creation start time. New features: - implement password roundtrip, #695 Bug fixes: - remote end does not need cache nor keys directories, do not create them, #701 - added retry counter for passwords, #703 Other changes: - fix compiler warnings, #697 - docs: - update README.rst to new changelog location in docs/changes.rst - add Teemu to AUTHORS - changes.rst: fix old chunker params, #698 - FAQ: how to limit bandwidth Version 1.0.0rc2 (2016-02-28) ----------------------------- New features: - format options for location: user, pid, fqdn, hostname, now, utcnow, user - borg list --list-format - borg prune -v --list enables the keep/prune list output, #658 Bug fixes: - fix _open_rb noatime handling, #657 - add a simple archivename validator, #680 - borg create --stats: show timestamps in localtime, use same labels/formatting as borg info, #651 - llfuse compatibility fixes (now compatible with: 0.40, 0.41, 0.42) Other changes: - it is now possible to use "pip install borgbackup[fuse]" to install the llfuse dependency automatically, using the correct version requirement for it. you still need to care about having installed the FUSE / build related OS package first, though, so that building llfuse can succeed. - Vagrant: drop Ubuntu Precise (12.04) - does not have Python >= 3.4 - Vagrant: use pyinstaller v3.1.1 to build binaries - docs: - borg upgrade: add to docs that only LOCAL repos are supported - borg upgrade also handles borg 0.xx -> 1.0 - use pip extras or requirements file to install llfuse - fix order in release process - updated usage docs and other minor / cosmetic fixes - verified borg examples in docs, #644 - freebsd dependency installation and FUSE configuration, #649 - add example how to restore a raw device, #671 - add a hint about the dev headers needed when installing from source - add examples for delete (and handle delete after list, before prune), #656 - update example for borg create -v --stats (use iso datetime format), #663 - added example to BORG_RSH docs - "connection closed by remote": add FAQ entry and point to issue #636 Version 1.0.0rc1 (2016-02-07) ----------------------------- New features: - borg migrate-to-repokey ("passphrase" -> "repokey" encryption key mode) - implement --short for borg list REPO, #611 - implement --list for borg extract (consistency with borg create) - borg serve: overwrite client's --restrict-to-path with ssh forced command's option value (but keep everything else from the client commandline), #544 - use $XDG_CONFIG_HOME/keys for keyfile keys (~/.config/borg/keys), #515 - "borg upgrade" moves the keyfile keys to the new location - display both archive creation start and end time in "borg info", #627 Bug fixes: - normalize trailing slashes for the repository path, #606 - Cache: fix exception handling in __init__, release lock, #610 Other changes: - suppress unneeded exception context (PEP 409), simpler tracebacks - removed special code needed to deal with imperfections / incompatibilities / missing stuff in py 3.2/3.3, simplify code that can be done simpler in 3.4 - removed some version requirements that were kept on old versions because newer did not support py 3.2 any more - use some py 3.4+ stdlib code instead of own/openssl/pypi code: - use os.urandom instead of own cython openssl RAND_bytes wrapper, #493 - use hashlib.pbkdf2_hmac from py stdlib instead of own openssl wrapper - use hmac.compare_digest instead of == operator (constant time comparison) - use stat.filemode instead of homegrown code - use "mock" library from stdlib, #145 - remove borg.support (with non-broken argparse copy), it is ok in 3.4+, #358 - Vagrant: copy CHANGES.rst as symlink, #592 - cosmetic code cleanups, add flake8 to tox/travis, #4 - docs / help: - make "borg -h" output prettier, #591 - slightly rephrase prune help - add missing example for --list option of borg create - quote exclude line that includes an asterisk to prevent shell expansion - fix dead link to license - delete Ubuntu Vivid, it is not supported anymore (EOL) - OS X binary does not work for older OS X releases, #629 - borg serve's special support for forced/original ssh commands, #544 - misc. updates and fixes ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/conf.py0000644000076500000240000002025614716225054014756 0ustar00twstaff# documentation build configuration file, created by # sphinx-quickstart on Sat Sep 10 18:18:25 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. import sys import os sys.path.insert(0, os.path.abspath("../src")) from borg import __version__ as sw_version # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "Borg - Deduplicating Archiver" copyright = "2010-2014 Jonas Borgström, 2015-2024 The Borg Collective (see AUTHORS file)" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. split_char = "+" if "+" in sw_version else "-" version = sw_version.split(split_char)[0] # The full version, including alpha/beta/rc tags. release = version suppress_warnings = ["image.nonlocal_uri"] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = "%Y-%m-%d" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None # The Borg docs contain no or very little Python docs. # Thus, the primary domain is rst. primary_domain = "rst" # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. import guzzle_sphinx_theme html_theme_path = guzzle_sphinx_theme.html_theme_path() html_theme = "guzzle_sphinx_theme" def set_rst_settings(app): app.env.settings.update({"field_name_limit": 0, "option_limit": 0}) def setup(app): app.add_css_file("css/borg.css") app.connect("builder-inited", set_rst_settings) # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = {"project_nav_name": "Borg %s" % version} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = "_static/logo.svg" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = "_static/favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["borg_theme"] html_extra_path = ["../src/borg/paperkey.html"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = "%Y-%m-%d" # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = True smartquotes_action = "qe" # no D in there means "do not transform -- and ---" # Custom sidebar templates, maps document names to template names. html_sidebars = {"**": ["logo-text.html", "searchbox.html", "globaltoc.html"]} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. html_use_index = False # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = False # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "borgdoc" # -- Options for LaTeX output -------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [("book", "Borg.tex", "Borg Documentation", "The Borg Collective", "manual")] # The name of an image file (relative to this directory) to place at the top of # the title page. latex_logo = "_static/logo.pdf" latex_elements = {"papersize": "a4paper", "pointsize": "10pt", "figure_align": "H"} # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. latex_show_urls = "footnote" # Additional stuff for the LaTeX preamble. # latex_preamble = '' # Documents to append as an appendix to all manuals. latex_appendices = ["support", "resources", "changes", "authors"] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ( "usage", "borg", "BorgBackup is a deduplicating backup program with optional compression and authenticated encryption.", ["The Borg Collective (see AUTHORS file)"], 1, ) ] extensions = [ "sphinx.ext.extlinks", "sphinx.ext.autodoc", "sphinx.ext.todo", "sphinx.ext.coverage", "sphinx.ext.viewcode", ] extlinks = { "issue": ("https://github.com/borgbackup/borg/issues/%s", "#%s"), "targz_url": ("https://pypi.python.org/packages/source/b/borgbackup/%%s-%s.tar.gz" % version, None), } ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1731802199.598301 borgbackup-2.0.0b14/docs/deployment/0000755000076500000240000000000014716232130015623 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/deployment/automated-local.rst0000644000076500000240000001712214716225054021442 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none Automated backups to a local hard drive ======================================= This guide shows how to automate backups to a hard drive directly connected to your computer. If a backup hard drive is connected, backups are automatically started, and the drive shut-down and disconnected when they are done. This guide is written for a Linux-based operating system and makes use of systemd and udev. Overview -------- An udev rule is created to trigger on the addition of block devices. The rule contains a tag that triggers systemd to start a oneshot service. The oneshot service executes a script in the standard systemd service environment, which automatically captures stdout/stderr and logs it to the journal. The script mounts the added block device, if it is a registered backup drive, and creates backups on it. When done, it optionally unmounts the file system and spins the drive down, so that it may be physically disconnected. Configuring the system ---------------------- First, create the ``/etc/backups`` directory (as root). All configuration goes into this directory. Find out the ID of the partition table of your backup disk (here assumed to be /dev/sdz): lsblk --fs -o +PTUUID /dev/sdz Then, create ``/etc/backups/40-backup.rules`` with the following content (all on one line):: ACTION=="add", SUBSYSTEM=="block", ENV{ID_PART_TABLE_UUID}=="", TAG+="systemd", ENV{SYSTEMD_WANTS}="automatic-backup.service" The "systemd" tag in conjunction with the SYSTEMD_WANTS environment variable has systemd launch the "automatic-backup" service, which we will create next, as the ``/etc/backups/automatic-backup.service`` file: .. code-block:: ini [Service] Type=oneshot ExecStart=/etc/backups/run.sh Now, create the main backup script, ``/etc/backups/run.sh``. Below is a template, modify it to suit your needs (e.g. more backup sets, dumping databases etc.). .. code-block:: bash #!/bin/bash -ue # The udev rule is not terribly accurate and may trigger our service before # the kernel has finished probing partitions. Sleep for a bit to ensure # the kernel is done. # # This can be avoided by using a more precise udev rule, e.g. matching # a specific hardware path and partition. sleep 5 # # Script configuration # # The backup partition is mounted there MOUNTPOINT=/mnt/backup # This is the location of the Borg repository TARGET=$MOUNTPOINT/borg-backups/backup.borg # Archive name schema DATE=$(date --iso-8601)-$(hostname) # This is the file that will later contain UUIDs of registered backup drives DISKS=/etc/backups/backup.disks # Find whether the connected block device is a backup drive for uuid in $(lsblk --noheadings --list --output uuid) do if grep --quiet --fixed-strings $uuid $DISKS; then break fi uuid= done if [ ! $uuid ]; then echo "No backup disk found, exiting" exit 0 fi echo "Disk $uuid is a backup disk" partition_path=/dev/disk/by-uuid/$uuid # Mount file system if not already done. This assumes that if something is already # mounted at $MOUNTPOINT, it is the backup drive. It won't find the drive if # it was mounted somewhere else. findmnt $MOUNTPOINT >/dev/null || mount $partition_path $MOUNTPOINT drive=$(lsblk --inverse --noheadings --list --paths --output name $partition_path | head --lines 1) echo "Drive path: $drive" # # Create backups # # Options for borg create BORG_OPTS="--stats --one-file-system --compression lz4" # Set BORG_PASSPHRASE or BORG_PASSCOMMAND somewhere around here, using export, # if encryption is used. # No one can answer if Borg asks these questions, it is better to just fail quickly # instead of hanging. export BORG_RELOCATED_REPO_ACCESS_IS_OK=no export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no # Log Borg version borg --version echo "Starting backup for $DATE" # This is just an example, change it however you see fit borg create $BORG_OPTS \ --exclude root/.cache \ --exclude var/lib/docker/devicemapper \ $TARGET::$DATE-$$-system \ / /boot # /home is often a separate partition / file system. # Even if it isn't (add --exclude /home above), it probably makes sense # to have /home in a separate archive. borg create $BORG_OPTS \ --exclude 'sh:home/*/.cache' \ $TARGET::$DATE-$$-home \ /home/ echo "Completed backup for $DATE" # Just to be completely paranoid sync if [ -f /etc/backups/autoeject ]; then umount $MOUNTPOINT hdparm -Y $drive fi if [ -f /etc/backups/backup-suspend ]; then systemctl suspend fi Create the ``/etc/backups/autoeject`` file to have the script automatically eject the drive after creating the backup. Rename the file to something else (e.g. ``/etc/backups/autoeject-no``) when you want to do something with the drive after creating backups (e.g running check). Create the ``/etc/backups/backup-suspend`` file if the machine should suspend after completing the backup. Don't forget to disconnect the device physically before resuming, otherwise you'll enter a cycle. You can also add an option to power down instead. Create an empty ``/etc/backups/backup.disks`` file, you'll register your backup drives there. The last part is actually to enable the udev rules and services: .. code-block:: bash ln -s /etc/backups/40-backup.rules /etc/udev/rules.d/40-backup.rules ln -s /etc/backups/automatic-backup.service /etc/systemd/system/automatic-backup.service systemctl daemon-reload udevadm control --reload Adding backup hard drives ------------------------- Connect your backup hard drive. Format it, if not done already. Find the UUID of the file system that backups should be stored on:: lsblk -o+uuid,label Note the UUID into the ``/etc/backups/backup.disks`` file. Mount the drive to /mnt/backup. Initialize a Borg repository at the location indicated by ``TARGET``:: borg init --encryption ... /mnt/backup/borg-backups/backup.borg Unmount and reconnect the drive, or manually start the ``automatic-backup`` service to start the first backup:: systemctl start --no-block automatic-backup See backup logs using journalctl:: journalctl -fu automatic-backup [-n number-of-lines] Security considerations ----------------------- The script as shown above will mount any file system with an UUID listed in ``/etc/backups/backup.disks``. The UUID check is a safety / annoyance-reduction mechanism to keep the script from blowing up whenever a random USB thumb drive is connected. It is not meant as a security mechanism. Mounting file systems and reading repository data exposes additional attack surfaces (kernel file system drivers, possibly user space services and Borg itself). On the other hand, someone standing right next to your computer can attempt a lot of attacks, most of which are easier to do than e.g. exploiting file systems (installing a physical key logger, DMA attacks, stealing the machine, ...). Borg ensures that backups are not created on random drives that "just happen" to contain a Borg repository. If an unknown unencrypted repository is encountered, then the script aborts (BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no). Backups are only created on hard drives that contain a Borg repository that is either known (by ID) to your machine or you are using encryption and the passphrase of the repository has to match the passphrase supplied to Borg. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/deployment/central-backup-server.rst0000644000076500000240000001713114716225054022566 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _central-backup-server: Central repository server with Ansible or Salt ============================================== This section will give an example how to set up a borg repository server for multiple clients. Machines -------- There are multiple machines used in this section and will further be named by their respective fully qualified domain name (fqdn). * The backup server: `backup01.srv.local` * The clients: - John Doe's desktop: `johndoe.clnt.local` - Webserver 01: `web01.srv.local` - Application server 01: `app01.srv.local` User and group -------------- The repository server needs to have only one UNIX user for all the clients. Recommended user and group with additional settings: * User: `backup` * Group: `backup` * Shell: `/bin/bash` (or other capable to run the `borg serve` command) * Home: `/home/backup` Most clients shall initiate a backup from the root user to catch all users, groups and permissions (e.g. when backing up `/home`). Folders ------- The following folder tree layout is suggested on the repository server: * User home directory, /home/backup * Repositories path (storage pool): /home/backup/repos * Clients restricted paths (`/home/backup/repos/`): - johndoe.clnt.local: `/home/backup/repos/johndoe.clnt.local` - web01.srv.local: `/home/backup/repos/web01.srv.local` - app01.srv.local: `/home/backup/repos/app01.srv.local` Restrictions ------------ Borg is instructed to restrict clients into their own paths: ``borg serve --restrict-to-path /home/backup/repos/`` The client will be able to access any file or subdirectory inside of ``/home/backup/repos/`` but no other directories. You can allow a client to access several separate directories by passing multiple ``--restrict-to-path`` flags, for instance: ``borg serve --restrict-to-path /home/backup/repos/ --restrict-to-path /home/backup/repos/``, which could make sense if multiple machines belong to one person which should then have access to all the backups of their machines. There is only one ssh key per client allowed. Keys are added for ``johndoe.clnt.local``, ``web01.srv.local`` and ``app01.srv.local``. But they will access the backup under only one UNIX user account as: ``backup@backup01.srv.local``. Every key in ``$HOME/.ssh/authorized_keys`` has a forced command and restrictions applied as shown below: :: command="cd /home/backup/repos/; borg serve --restrict-to-path /home/backup/repos/", restrict .. note:: The text shown above needs to be written on a single line! The options which are added to the key will perform the following: 1. Change working directory 2. Run ``borg serve`` restricted to the client base path 3. Restrict ssh and do not allow stuff which imposes a security risk Due to the ``cd`` command we use, the server automatically changes the current working directory. Then client doesn't need to have knowledge of the absolute or relative remote repository path and can directly access the repositories at ``ssh://@/./``. .. note:: The setup above ignores all client given commandline parameters which are normally appended to the `borg serve` command. Client ------ The client needs to initialize the `pictures` repository like this: :: borg init ssh://backup@backup01.srv.local/./pictures Or with the full path (should actually never be used, as only for demonstration purposes). The server should automatically change the current working directory to the `` folder. :: borg init ssh://backup@backup01.srv.local/home/backup/repos/johndoe.clnt.local/pictures When `johndoe.clnt.local` tries to access a not restricted path the following error is raised. John Doe tries to back up into the Web 01 path: :: borg init ssh://backup@backup01.srv.local/home/backup/repos/web01.srv.local/pictures :: ~~~ SNIP ~~~ Remote: borg.remote.PathNotAllowed: /home/backup/repos/web01.srv.local/pictures ~~~ SNIP ~~~ Repository path not allowed Ansible ------- Ansible takes care of all the system-specific commands to add the user, create the folder, install and configure software. :: - hosts: backup01.srv.local vars: user: backup group: backup home: /home/backup pool: "{{ home }}/repos" auth_users: - host: johndoe.clnt.local key: "{{ lookup('file', '/path/to/keys/johndoe.clnt.local.pub') }}" - host: web01.clnt.local key: "{{ lookup('file', '/path/to/keys/web01.clnt.local.pub') }}" - host: app01.clnt.local key: "{{ lookup('file', '/path/to/keys/app01.clnt.local.pub') }}" tasks: - package: name=borg state=present - group: name="{{ group }}" state=present - user: name="{{ user }}" shell=/bin/bash home="{{ home }}" createhome=yes group="{{ group }}" groups= state=present - file: path="{{ home }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory - file: path="{{ home }}/.ssh" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory - file: path="{{ pool }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory - authorized_key: user="{{ user }}" key="{{ item.key }}" key_options='command="cd {{ pool }}/{{ item.host }};borg serve --restrict-to-path {{ pool }}/{{ item.host }}",restrict' with_items: "{{ auth_users }}" - file: path="{{ home }}/.ssh/authorized_keys" owner="{{ user }}" group="{{ group }}" mode=0600 state=file - file: path="{{ pool }}/{{ item.host }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory with_items: "{{ auth_users }}" Salt ---- This is a configuration similar to the one above, configured to be deployed with Salt running on a Debian system. :: Install borg backup from pip: pkg.installed: - pkgs: - python3 - python3-dev - python3-pip - python-virtualenv - libssl-dev - openssl - libacl1-dev - libacl1 - build-essential - libfuse-dev - fuse - pkg-config pip.installed: - pkgs: ["borgbackup"] - bin_env: /usr/bin/pip3 Setup backup user: user.present: - name: backup - fullname: Backup User - home: /home/backup - shell: /bin/bash # CAUTION! # If you change the ssh command= option below, it won't necessarily get pushed to the backup # server correctly unless you delete the ~/.ssh/authorized_keys file and re-create it! {% for host in backupclients %} Give backup access to {{host}}: ssh_auth.present: - user: backup - source: salt://conf/ssh-pubkeys/{{host}}-backup.id_ecdsa.pub - options: - command="cd /home/backup/repos/{{host}}; borg serve --restrict-to-path /home/backup/repos/{{host}}" - restrict {% endfor %} Enhancements ------------ As this section only describes a simple and effective setup, it could be further enhanced when supporting (a limited set) of client supplied commands. A wrapper for starting `borg serve` could be written. Or borg itself could be enhanced to autodetect it runs under SSH by checking the `SSH_ORIGINAL_COMMAND` environment variable. This is left open for future improvements. When extending ssh autodetection in borg no external wrapper script is necessary and no other interpreter or application has to be deployed. See also -------- * `SSH Daemon manpage `_ * `Ansible `_ * `Salt `_ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/deployment/hosting-repositories.rst0000644000076500000240000000646314716225054022575 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _hosting_repositories: Hosting repositories ==================== This sections shows how to provide repository storage securely for users. Optionally, each user can have a storage quota. Repositories are accessed through SSH. Each user of the service should have her own login which is only able to access the user's files. Technically it would be possible to have multiple users share one login, however, separating them is better. Separate logins increase isolation and are thus an additional layer of security and safety for both the provider and the users. For example, if a user manages to breach ``borg serve`` then she can only damage her own data (assuming that the system does not have further vulnerabilities). Use the standard directory structure of the operating system. Each user is assigned a home directory and repositories of the user reside in her home directory. The following ``~user/.ssh/authorized_keys`` file is the most important piece for a correct deployment. It allows the user to log in via their public key (which must be provided by the user), and restricts SSH access to safe operations only. :: command="borg serve --restrict-to-repository /home//repository",restrict .. note:: The text shown above needs to be written on a **single** line! .. warning:: If this file should be automatically updated (e.g. by a web console), pay **utmost attention** to sanitizing user input. Strip all whitespace around the user-supplied key, ensure that it **only** contains ASCII with no control characters and that it consists of three parts separated by a single space. Ensure that no newlines are contained within the key. The ``restrict`` keyword enables all restrictions, i.e. disables port, agent and X11 forwarding, as well as disabling PTY allocation and execution of ~/.ssh/rc. If any future restriction capabilities are added to authorized_keys files they will be included in this set. The ``command`` keyword forces execution of the specified command line upon login. This must be ``borg serve``. The ``--restrict-to-repository`` option permits access to exactly **one** repository. It can be given multiple times to permit access to more than one repository. The repository may not exist yet; it can be initialized by the user, which allows for encryption. **Storage quotas** can be enabled by adding the ``--storage-quota`` option to the ``borg serve`` command line:: restrict,command="borg serve --storage-quota 20G ..." ... The storage quotas of repositories are completely independent. If a client is able to access multiple repositories, each repository can be filled to the specified quota. If storage quotas are used, ensure that all deployed Borg releases support storage quotas. **Specificities: Append-only repositories** Running ``borg init`` via a ``borg serve --append-only`` server will **not** create a repository that is configured to be append-only by its repository config. But, ``--append-only`` arguments in ``authorized_keys`` will override the repository config, therefore append-only mode can be enabled on a key by key basis. Refer to the `sshd(8) `_ man page for more details on SSH options. See also :ref:`borg_serve` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/deployment/image-backup.rst0000644000076500000240000001576614716225054020730 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none Backing up entire disk images ============================= Backing up disk images can still be efficient with Borg because its `deduplication`_ technique makes sure only the modified parts of the file are stored. Borg also has optional simple sparse file support for extract. It is of utmost importancy to pin down the disk you want to backup. You need to use the SERIAL for that. Use: .. code-block:: bash # You can find the short disk serial by: # udevadm info --query=property --name=nvme1n1 | grep ID_SERIAL_SHORT | cut -d '=' -f 2 export BORG_REPO=/path/to/repo DISK_SERIAL="7VS0224F" DISK_ID=$(readlink -f /dev/disk/by-id/*"${DISK_SERIAL}") # Returns /dev/nvme1n1 mapfile -t PARTITIONS < <(lsblk -o NAME,TYPE -p -n -l "$DISK_ID" | awk '$2 == "part" {print $1}') echo "Partitions of $DISK_ID:" echo "${PARTITIONS[@]}" echo "Disk Identifier: $DISK_ID" # Use the following line to perform a borg backup for the full disk: # borg create --read-special disk-backup "$DISK_ID" # Use the following to perform a borg backup for all partitions of the disk # borg create --read-special partitions-backup "${PARTITIONS[@]}" # Example output: # Partitions of /dev/nvme1n1: # /dev/nvme1n1p1 # /dev/nvme1n1p2 # /dev/nvme1n1p3 # Disk Identifier: /dev/nvme1n1 # borg create --read-special disk-backup /dev/nvme1n1 # borg create --read-special partitions-backup /dev/nvme1n1p1 /dev/nvme1n1p2 /dev/nvme1n1p3 Decreasing the size of image backups ------------------------------------ Disk images are as large as the full disk when uncompressed and might not get much smaller post-deduplication after heavy use because virtually all file systems don't actually delete file data on disk but instead delete the filesystem entries referencing the data. Therefore, if a disk nears capacity and files are deleted again, the change will barely decrease the space it takes up when compressed and deduplicated. Depending on the filesystem, there are several ways to decrease the size of a disk image: Using ntfsclone (NTFS, i.e. Windows VMs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``ntfsclone`` can only operate on filesystems with the journal cleared (i.e. turned-off machines), which somewhat limits its utility in the case of VM snapshots. However, when it can be used, its special image format is even more efficient than just zeroing and deduplicating. For backup, save the disk header and the contents of each partition:: HEADER_SIZE=$(sfdisk -lo Start $DISK | grep -A1 -P 'Start$' | tail -n1 | xargs echo) PARTITIONS=$(sfdisk -lo Device,Type $DISK | sed -e '1,/Device\s*Type/d') dd if=$DISK count=$HEADER_SIZE | borg create repo::hostname-partinfo - echo "$PARTITIONS" | grep NTFS | cut -d' ' -f1 | while read x; do PARTNUM=$(echo $x | grep -Eo "[0-9]+$") ntfsclone -so - $x | borg create repo::hostname-part$PARTNUM - done # to back up non-NTFS partitions as well: echo "$PARTITIONS" | grep -v NTFS | cut -d' ' -f1 | while read x; do PARTNUM=$(echo $x | grep -Eo "[0-9]+$") borg create --read-special repo::hostname-part$PARTNUM $x done Restoration is a similar process:: borg extract --stdout repo::hostname-partinfo | dd of=$DISK && partprobe PARTITIONS=$(sfdisk -lo Device,Type $DISK | sed -e '1,/Device\s*Type/d') borg list --format {archive}{NL} repo | grep 'part[0-9]*$' | while read x; do PARTNUM=$(echo $x | grep -Eo "[0-9]+$") PARTITION=$(echo "$PARTITIONS" | grep -E "$DISKp?$PARTNUM" | head -n1) if echo "$PARTITION" | cut -d' ' -f2- | grep -q NTFS; then borg extract --stdout repo::$x | ntfsclone -rO $(echo "$PARTITION" | cut -d' ' -f1) - else borg extract --stdout repo::$x | dd of=$(echo "$PARTITION" | cut -d' ' -f1) fi done .. note:: When backing up a disk image (as opposed to a real block device), mount it as a loopback image to use the above snippets:: DISK=$(losetup -Pf --show /path/to/disk/image) # do backup as shown above losetup -d $DISK Using zerofree (ext2, ext3, ext4) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``zerofree`` works similarly to ntfsclone in that it zeros out unused chunks of the FS, except it works in place, zeroing the original partition. This makes the backup process a bit simpler:: sfdisk -lo Device,Type $DISK | sed -e '1,/Device\s*Type/d' | grep Linux | cut -d' ' -f1 | xargs -n1 zerofree borg create --read-special repo::hostname-disk $DISK Because the partitions were zeroed in place, restoration is only one command:: borg extract --stdout repo::hostname-disk | dd of=$DISK .. note:: The "traditional" way to zero out space on a partition, especially one already mounted, is simply to ``dd`` from ``/dev/zero`` to a temporary file and delete it. This is ill-advised for the reasons mentioned in the ``zerofree`` man page: - it is slow - it makes the disk image (temporarily) grow to its maximal extent - it (temporarily) uses all free space on the disk, so other concurrent write actions may fail. Virtual machines ---------------- If you use non-snapshotting backup tools like Borg to back up virtual machines, then the VMs should be turned off for the duration of the backup. Backing up live VMs can (and will) result in corrupted or inconsistent backup contents: a VM image is just a regular file to Borg with the same issues as regular files when it comes to concurrent reading and writing from the same file. For backing up live VMs use filesystem snapshots on the VM host, which establishes crash-consistency for the VM images. This means that with most file systems (that are journaling) the FS will always be fine in the backup (but may need a journal replay to become accessible). Usually this does not mean that file *contents* on the VM are consistent, since file contents are normally not journaled. Notable exceptions are ext4 in data=journal mode, ZFS and btrfs (unless nodatacow is used). Applications designed with crash-consistency in mind (most relational databases like PostgreSQL, SQLite etc. but also for example Borg repositories) should always be able to recover to a consistent state from a backup created with crash-consistent snapshots (even on ext4 with data=writeback or XFS). Other applications may require a lot of work to reach application-consistency; it's a broad and complex issue that cannot be explained in entirety here. Hypervisor snapshots capturing most of the VM's state can also be used for backups and can be a better alternative to pure file system based snapshots of the VM's disk, since no state is lost. Depending on the application this can be the easiest and most reliable way to create application-consistent backups. Borg doesn't intend to address these issues due to their huge complexity and platform/software dependency. Combining Borg with the mechanisms provided by the platform (snapshots, hypervisor features) will be the best approach to start tackling them. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/deployment/non-root-user.rst0000644000076500000240000000465314716225054021123 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _non_root_user: ================================ Backing up using a non-root user ================================ This section describes how to run borg as a non-root user and still be able to backup every file on the system. Normally borg is run as the root user to bypass all filesystem permissions and be able to read all files. But in theory this also allows borg to modify or delete files on your system, in case of a bug for example. To eliminate this possibility, we can run borg as a non-root user and give it read-only permissions to all files on the system. Using Linux capabilities inside a systemd service ================================================= One way to do so, is to use linux `capabilities `_ within a systemd service. Linux capabilities allow us to give parts of the privileges the root user has to a non-root user. This works on a per-thread level and does not give the permission to the non-root user as a whole. For this we need to run our backup script from a systemd service and use the `AmbientCapabilities `_ option added in systemd 229. A very basic unit file would look like this: :: [Unit] Description=Borg Backup [Service] Type=oneshot User=borg ExecStart=/usr/local/sbin/backup.sh AmbientCapabilities=CAP_DAC_READ_SEARCH The ``CAP_DAC_READ_SEARCH`` capability gives borg read-only access to all files and directories on the system. This service can then be started manually using ``systemctl start``, a systemd timer or other methods. Restore considerations ====================== When restoring files, the root user should be used. When using the non-root user, borg extract will change all files to be owned by the non-root user. Using borg mount will not allow the non-root user to access files that it would not have access to on the system itself. Other than that, the same restore process, that would be used when running the backup as root, can be used. .. warning:: When using a local repo and running borg commands as root, make sure to only use commands that do not modify the repo itself, like extract or mount. Modifying the repo using the root user will break the repo for the non-root user, since some files inside the repo will now be owned by root. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/deployment/pull-backup.rst0000644000076500000240000005063014716225054020607 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _pull_backup: ======================= Backing up in pull mode ======================= Typically the borg client connects to a backup server using SSH as a transport when initiating a backup. This is referred to as push mode. If you however require the backup server to initiate the connection or prefer it to initiate the backup run, one of the following workarounds is required to allow such a pull mode setup. A common use case for pull mode is to back up a remote server to a local personal computer. SSHFS ===== Assuming you have a pull backup system set up with borg, where a backup server pulls the data from the target via SSHFS. In this mode, the backup client's file system is mounted remotely on the backup server. Pull mode is even possible if the SSH connection must be established by the client via a remote tunnel. Other network file systems like NFS or SMB could be used as well, but SSHFS is very simple to set up and probably the most secure one. There are some restrictions caused by SSHFS. For example, unless you define UID and GID mappings when mounting via ``sshfs``, owners and groups of the mounted file system will probably change, and you may not have access to those files if BorgBackup is not run with root privileges. SSHFS is a FUSE file system and uses the SFTP protocol, so there may be also other unsupported features that the actual implementations of sshfs, libfuse and sftp on the backup server do not support, like file name encodings, ACLs, xattrs or flags. So there is no guarantee that you are able to restore a system completely in every aspect from such a backup. .. warning:: To mount the client's root file system you will need root access to the client. This contradicts to the usual threat model of BorgBackup, where clients don't need to trust the backup server (data is encrypted). In pull mode the server (when logged in as root) could cause unlimited damage to the client. Therefore, pull mode should be used only from servers you do fully trust! .. warning:: Additionally, while being chrooted into the client's root file system, code from the client will be executed. Thus, you should only do that when fully trusting the client. .. warning:: The chroot method was chosen to get the right user and group name-id mappings, assuming they only come from files (/etc/passwd and group). This assumption might be wrong, e.g. if users/groups also come from ldap or other providers. Thus, it might be better to use ``--numeric-ids`` and not archive any user or group names (but just the numeric IDs) and not use chroot. Creating a backup ----------------- Generally, in a pull backup situation there is no direct way for borg to know the client's original UID:GID name mapping of files, because Borg would use ``/etc/passwd`` and ``/etc/group`` of the backup server to map the names. To derive the right names, Borg needs to have access to the client's passwd and group files and use them in the backup process. The solution to this problem is chrooting into an sshfs mounted directory. In this example the whole client root file system is mounted. We use the stand-alone BorgBackup executable and copy it into the mounted file system to make Borg available after entering chroot; this can be skipped if Borg is already installed on the client. :: # Mount client root file system. mkdir /tmp/sshfs sshfs root@host:/ /tmp/sshfs # Mount BorgBackup repository inside it. mkdir /tmp/sshfs/borgrepo mount --bind /path/to/repo /tmp/sshfs/borgrepo # Make borg executable available. cp /usr/local/bin/borg /tmp/sshfs/usr/local/bin/borg # Mount important system directories and enter chroot. cd /tmp/sshfs for i in dev proc sys; do mount --bind /$i $i; done chroot /tmp/sshfs Now we are on the backup system but inside a chroot with the client's root file system. We have a copy of Borg binary in ``/usr/local/bin`` and the repository in ``/borgrepo``. Borg will back up the client's user/group names, and we can create the backup, retaining the original paths, excluding the repository: :: borg create --exclude borgrepo --files-cache ctime,size /borgrepo::archive / For the sake of simplicity only ``borgrepo`` is excluded here. You may want to set up an exclude file with additional files and folders to be excluded. Also note that we have to modify Borg's file change detection behaviour – SSHFS cannot guarantee stable inode numbers, so we have to supply the ``--files-cache`` option. Finally, we need to exit chroot, unmount all the stuff and clean up: :: exit # exit chroot rm /tmp/sshfs/usr/local/bin/borg cd /tmp/sshfs for i in dev proc sys borgrepo; do umount ./$i; done rmdir borgrepo cd ~ umount /tmp/sshfs rmdir /tmp/sshfs Thanks to secuser on IRC for this how-to! Restore methods --------------- The counterpart of a pull backup is a push restore. Depending on the type of restore – full restore or partial restore – there are different methods to make sure the correct IDs are restored. Partial restore ~~~~~~~~~~~~~~~ In case of a partial restore, using the archived UIDs/GIDs might lead to wrong results if the name-to-ID mapping on the target system has changed compared to backup time (might be the case e.g. for a fresh OS install). The workaround again is chrooting into an sshfs mounted directory, so Borg is able to map the user/group names of the backup files to the actual IDs on the client. This example is similar to the backup above – only the Borg command is different: :: # Mount client root file system. mkdir /tmp/sshfs sshfs root@host:/ /tmp/sshfs # Mount BorgBackup repository inside it. mkdir /tmp/sshfs/borgrepo mount --bind /path/to/repo /tmp/sshfs/borgrepo # Make borg executable available. cp /usr/local/bin/borg /tmp/sshfs/usr/local/bin/borg # Mount important system directories and enter chroot. cd /tmp/sshfs for i in dev proc sys; do mount --bind /$i $i; done chroot /tmp/sshfs Now we can run :: borg extract /borgrepo::archive PATH to restore whatever we like partially. Finally, do the clean-up: :: exit # exit chroot rm /tmp/sshfs/usr/local/bin/borg cd /tmp/sshfs for i in dev proc sys borgrepo; do umount ./$i; done rmdir borgrepo cd ~ umount /tmp/sshfs rmdir /tmp/sshfs Full restore ~~~~~~~~~~~~ When doing a full restore, we restore all files (including the ones containing the ID-to-name mapping, ``/etc/passwd`` and ``/etc/group``). Everything will be consistent automatically if we restore the numeric IDs stored in the archive. So there is no need for a chroot environment; we just mount the client file system and extract a backup, utilizing the ``--numeric-ids`` option: :: sshfs root@host:/ /mnt/sshfs cd /mnt/sshfs borg extract --numeric-ids /path/to/repo::archive cd ~ umount /mnt/sshfs Simple (lossy) full restore ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Using ``borg export-tar`` it is possible to stream a backup to the client and directly extract it without the need of mounting with SSHFS: :: borg export-tar /path/to/repo::archive - | ssh root@host 'tar -C / -x' Note that in this scenario the tar format is the limiting factor – it cannot restore all the advanced features that BorgBackup supports. See :ref:`borg_export-tar` for limitations. socat ===== In this setup a SSH connection from the backup server to the client is established that uses SSH reverse port forwarding to tunnel data transparently between UNIX domain sockets on the client and server and the socat tool to connect these with the borg client and server processes, respectively. The program socat has to be available on the backup server and on the client to be backed up. When **pushing** a backup the borg client (holding the data to be backed up) connects to the backup server via ssh, starts ``borg serve`` on the backup server and communicates via standard input and output (transported via SSH) with the process on the backup server. With the help of socat this process can be reversed. The backup server will create a connection to the client (holding the data to be backed up) and will **pull** the data. In the following example *borg-server* connects to *borg-client* to pull a backup. To provide a secure setup sockets should be stored in ``/run/borg``, only accessible to the users that run the backup process. So on both systems, *borg-server* and *borg-client* the folder ``/run/borg`` has to be created:: sudo mkdir -m 0700 /run/borg On *borg-server* the socket file is opened by the user running the ``borg serve`` process writing to the repository so the user has to have read and write permissions on ``/run/borg``:: borg-server:~$ sudo chown borgs /run/borg On *borg-client* the socket file is created by ssh, so the user used to connect to *borg-client* has to have read and write permissions on ``/run/borg``:: borg-client:~$ sudo chown borgc /run/borg On *borg-server*, we have to start the command ``borg serve`` and make its standard input and output available to a unix socket:: borg-server:~$ socat UNIX-LISTEN:/run/borg/reponame.sock,fork EXEC:"borg serve --append-only --restrict-to-path /path/to/repo" Socat will wait until a connection is opened. Then socat will execute the command given, redirecting Standard Input and Output to the unix socket. The optional arguments for ``borg serve`` are not necessary but a sane default. .. note:: When used in production you may also use systemd socket-based activation instead of socat on the server side. You would wrap the ``borg serve`` command in a `service unit`_ and configure a matching `socket unit`_ to start the service whenever a client connects to the socket. .. _service unit: https://www.freedesktop.org/software/systemd/man/systemd.service.html .. _socket unit: https://www.freedesktop.org/software/systemd/man/systemd.socket.html Now we need a way to access the unix socket on *borg-client* (holding the data to be backed up), as we created the unix socket on *borg-server* Opening a SSH connection from the *borg-server* to the *borg-client* with reverse port forwarding can do this for us:: borg-server:~$ ssh -R /run/borg/reponame.sock:/run/borg/reponame.sock borgc@borg-client .. note:: As the default value of OpenSSH for ``StreamLocalBindUnlink`` is ``no``, the socket file created by sshd is not removed. Trying to connect a second time, will print a short warning, and the forwarding does **not** take place:: Warning: remote port forwarding failed for listen path /run/borg/reponame.sock When you are done, you have to remove the socket file manually, otherwise you may see an error like this when trying to execute borg commands:: Remote: YYYY/MM/DD HH:MM:SS socat[XXX] E connect(5, AF=1 "/run/borg/reponame.sock", 13): Connection refused Connection closed by remote host. Is borg working on the server? When a process opens the socket on *borg-client*, SSH will forward all data to the socket on *borg-server*. The next step is to tell borg on *borg-client* to use the unix socket to communicate with the ``borg serve`` command on *borg-server* via the socat socket instead of SSH:: borg-client:~$ export BORG_RSH="sh -c 'exec socat STDIO UNIX-CONNECT:/run/borg/reponame.sock'" The default value for ``BORG_RSH`` is ``ssh``. By default Borg uses SSH to create the connection to the backup server. Therefore Borg parses the repo URL and adds the server name (and other arguments) to the SSH command. Those arguments can not be handled by socat. We wrap the command with ``sh`` to ignore all arguments intended for the SSH command. All Borg commands can now be executed on *borg-client*. For example to create a backup execute the ``borg create`` command:: borg-client:~$ borg create ssh://borg-server/path/to/repo::archive /path_to_backup When automating backup creation, the interactive ssh session may seem inappropriate. An alternative way of creating a backup may be the following command:: borg-server:~$ ssh \ -R /run/borg/reponame.sock:/run/borg/reponame.sock \ borgc@borg-client \ borg create \ --rsh "sh -c 'exec socat STDIO UNIX-CONNECT:/run/borg/reponame.sock'" \ ssh://borg-server/path/to/repo::archive /path_to_backup \ ';' rm /run/borg/reponame.sock This command also automatically removes the socket file after the ``borg create`` command is done. ssh-agent ========= In this scenario *borg-server* initiates an SSH connection to *borg-client* and forwards the authentication agent connection. After that, it works similar to the push mode: *borg-client* initiates another SSH connection back to *borg-server* using the forwarded authentication agent connection to authenticate itself, starts ``borg serve`` and communicates with it. Using this method requires ssh access of user *borgs* to *borgc@borg-client*, where: * *borgs* is the user on the server side with read/write access to local borg repository. * *borgc* is the user on the client side with read access to files meant to be backed up. Applying this method for automated backup operations ---------------------------------------------------- Assume that the borg-client host is untrusted. Therefore we do some effort to prevent a hostile user on the borg-client side to do something harmful. In case of a fully trusted borg-client the method could be simplified. Preparing the server side ~~~~~~~~~~~~~~~~~~~~~~~~~ Do this once for each client on *borg-server* to allow *borgs* to connect itself on *borg-server* using a dedicated ssh key: :: borgs@borg-server$ install -m 700 -d ~/.ssh/ borgs@borg-server$ ssh-keygen -N '' -t rsa -f ~/.ssh/borg-client_key borgs@borg-server$ { echo -n 'command="borg serve --append-only --restrict-to-repo ~/repo",restrict '; cat ~/.ssh/borg-client_key.pub; } >> ~/.ssh/authorized_keys borgs@borg-server$ chmod 600 ~/.ssh/authorized_keys ``install -m 700 -d ~/.ssh/`` Create directory ~/.ssh with correct permissions if it does not exist yet. ``ssh-keygen -N '' -t rsa -f ~/.ssh/borg-client_key`` Create an ssh key dedicated to communication with borg-client. .. note:: Another more complex approach is using a unique ssh key for each pull operation. This is more secure as it guarantees that the key will not be used for other purposes. ``{ echo -n 'command="borg serve --append-only --restrict-to-repo ~/repo",restrict '; cat ~/.ssh/borg-client_key.pub; } >> ~/.ssh/authorized_keys`` Add borg-client's ssh public key to ~/.ssh/authorized_keys with forced command and restricted mode. The borg client is restricted to use one repo at the specified path and to append-only operation. Commands like *delete*, *prune* and *compact* have to be executed another way, for example directly on *borg-server* side or from a privileged, less restricted client (using another authorized_keys entry). ``chmod 600 ~/.ssh/authorized_keys`` Fix permissions of ~/.ssh/authorized_keys. Pull operation ~~~~~~~~~~~~~~ Initiating borg command execution from *borg-server* (e.g. init):: borgs@borg-server$ ( eval $(ssh-agent) > /dev/null ssh-add -q ~/.ssh/borg-client_key echo 'your secure borg key passphrase' | \ ssh -A -o StrictHostKeyChecking=no borgc@borg-client "BORG_PASSPHRASE=\$(cat) borg --rsh 'ssh -o StrictHostKeyChecking=no' init --encryption repokey ssh://borgs@borg-server/~/repo" kill "${SSH_AGENT_PID}" ) Parentheses around commands are needed to avoid interference with a possibly already running ssh-agent. Parentheses are not needed when using a dedicated bash process. ``eval $(ssh-agent) > /dev/null`` Run the SSH agent in the background and export related environment variables to the current bash session. ``ssh-add -q ~/.ssh/borg-client_key`` Load the SSH private key dedicated to communication with the borg-client into the SSH agent. Look at ``man 1 ssh-add`` for a more detailed explanation. .. note:: Care needs to be taken when loading keys into the SSH agent. Users on the *borg-client* having read/write permissions to the agent's UNIX-domain socket (at least borgc and root in our case) can access the agent on *borg-server* through the forwarded connection and can authenticate using any of the identities loaded into the agent (look at ``man 1 ssh`` for more detailed explanation). Therefore there are some security considerations: * Private keys loaded into the agent must not be used to enable access anywhere else. * The keys meant to be loaded into the agent must be specified explicitly, not from default locations. * The *borg-client*'s entry in *borgs@borg-server:~/.ssh/authorized_keys* must be as restrictive as possible. ``echo 'your secure borg key passphrase' | ssh -A -o StrictHostKeyChecking=no borgc@borg-client "BORG_PASSPHRASE=\$(cat) borg --rsh 'ssh -o StrictHostKeyChecking=no' init --encryption repokey ssh://borgs@borg-server/~/repo"`` Run the *borg init* command on *borg-client*. *ssh://borgs@borg-server/~/repo* refers to the repository *repo* within borgs's home directory on *borg-server*. *StrictHostKeyChecking=no* is used to add host keys automatically to *~/.ssh/known_hosts* without user intervention. ``kill "${SSH_AGENT_PID}"`` Kill ssh-agent with loaded keys when it is not needed anymore. Remote forwarding ================= The standard ssh client allows to create tunnels to forward local ports to a remote server (local forwarding) and also to allow remote ports to be forwarded to local ports (remote forwarding). This remote forwarding can be used to allow remote backup clients to access the backup server even if the backup server cannot be reached by the backup client. This can even be used in cases where neither the backup server can reach the backup client and the backup client cannot reach the backup server, but some intermediate host can access both. A schematic approach is as follows :: Backup Server (backup@mybackup) Intermediate Machine (john@myinter) Backup Client (bob@myclient) 1. Establish SSH remote forwarding -----------> SSH listen on local port 2. Starting ``borg create`` establishes 3. SSH forwards to intermediate machine <------- SSH connection to the local port 4. Receives backup connection <------- and further on to backup server via SSH So for the backup client the backup is done via SSH to a local port and for the backup server there is a normal backup performed via ssh. In order to achieve this, the following commands can be used to create the remote port forwarding: 1. On machine ``myinter`` ``ssh bob@myclient -v -C -R 8022:mybackup:22 -N`` This will listen for ssh-connections on port ``8022`` on ``myclient`` and forward connections to port 22 on ``mybackup``. You can also remove the need for machine ``myinter`` and create the port forwarding on the backup server directly by using ``localhost`` instead of ``mybackup`` 2. On machine ``myclient`` ``borg create -v --progress --stats ssh://backup@localhost:8022/home/backup/repos/myclient /`` Make sure to use port ``8022`` and ``localhost`` for the repository as this instructs borg on ``myclient`` to use the remote forwarded ssh connection. SSH Keys -------- If you want to automate backups when using this method, the ssh ``known_hosts`` and ``authorized_keys`` need to be set up to allow connections. Security Considerations ----------------------- Opening up SSH access this way can pose a security risk as it effectively opens remote access to your backup server on the client even if it is located outside of your company network. To reduce the chances of compromise, you should configure a forced command in ``authorized_keys`` to prevent anyone from performing any other action on the backup server. This can be done e.g. by adding the following in ``$HOME/.ssh/authorized_keys`` on ``mybackup`` with proper path and client-fqdn: :: command="cd /home/backup/repos/;borg serve --restrict-to-path /home/backup/repos/" All the additional security considerations for borg should be applied, see :ref:`central-backup-server` for some additional hints. More information ---------------- See `remote forwarding`_ and the `ssh man page`_ for more information about remote forwarding. .. _remote forwarding: https://linuxize.com/post/how-to-setup-ssh-tunneling/ .. _ssh man page: https://manpages.debian.org/testing/manpages-de/ssh.1.de.html ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/deployment.rst0000644000076500000240000000054514716225054016370 0ustar00twstaff.. include:: global.rst.inc .. highlight:: none Deployment ========== This chapter details deployment strategies for the following scenarios. .. toctree:: :titlesonly: deployment/central-backup-server deployment/hosting-repositories deployment/automated-local deployment/image-backup deployment/pull-backup deployment/non-root-user ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/development.rst0000644000076500000240000004033014716225054016526 0ustar00twstaff.. include:: global.rst.inc .. highlight:: bash .. _development: Development =========== This chapter will get you started with Borg development. Borg is written in Python (with a little bit of Cython and C for the performance critical parts). Contributions ------------- ... are welcome! Some guidance for contributors: - Discuss changes on the GitHub issue tracker, on IRC or on the mailing list. - Make your PRs on the ``master`` branch (see `Branching Model`_ for details). - Do clean changesets: - Focus on some topic, resist changing anything else. - Do not do style changes mixed with functional changes. - Try to avoid refactorings mixed with functional changes. - If you need to fix something after commit/push: - If there are ongoing reviews: do a fixup commit you can squash into the bad commit later. - If there are no ongoing reviews or you did not push the bad commit yet: amend the commit to include your fix or merge the fixup commit before pushing. - Have a nice, clear, typo-free commit comment. - If you fixed an issue, refer to it in your commit comment. - Follow the style guide (see below). - If you write new code, please add tests and docs for it. - Run the tests, fix any issues that come up. - Make a pull request on GitHub. - Wait for review by other developers. Branching model --------------- Borg development happens on the ``master`` branch and uses GitHub pull requests (if you don't have GitHub or don't want to use it you can send smaller patches via the borgbackup mailing list to the maintainers). Stable releases are maintained on maintenance branches named ``x.y-maint``, eg. the maintenance branch of the 1.2.x series is ``1.2-maint``. Most PRs should be filed against the ``master`` branch. Only if an issue affects **only** a particular maintenance branch a PR should be filed against it directly. While discussing / reviewing a PR it will be decided whether the change should be applied to maintenance branches. Each maintenance branch has a corresponding *backport/x.y-maint* label, which will then be applied. Changes that are typically considered for backporting: - Data loss, corruption and inaccessibility fixes. - Security fixes. - Forward-compatibility improvements. - Documentation corrections. .. rubric:: Maintainer part From time to time a maintainer will backport the changes for a maintenance branch, typically before a release or if enough changes were collected: 1. Notify others that you're doing this to avoid duplicate work. 2. Branch a backporting branch off the maintenance branch. 3. Cherry pick and backport the changes from each labelled PR, remove the label for each PR you've backported. To preserve authorship metadata, do not follow the ``git cherry-pick`` instructions to use ``git commit`` after resolving conflicts. Instead, stage conflict resolutions and run ``git cherry-pick --continue``, much like using ``git rebase``. To avoid merge issues (a cherry pick is a form of merge), use these options (similar to the ``git merge`` options used previously, the ``-x`` option adds a reference to the original commit):: git cherry-pick --strategy recursive -X rename-threshold=5% -x 4. Make a PR of the backporting branch against the maintenance branch for backport review. Mention the backported PRs in this PR, e.g.: Includes changes from #2055 #2057 #2381 This way GitHub will automatically show in these PRs where they were backported. .. rubric:: Historic model Previously (until release 1.0.10) Borg used a `"merge upwards" `_ model where most minor changes and fixes where committed to a maintenance branch (eg. 1.0-maint), and the maintenance branch(es) were regularly merged back into the main development branch. This became more and more troublesome due to merges growing more conflict-heavy and error-prone. How to submit a pull request ---------------------------- In order to contribute to Borg, you will need to fork the ``borgbackup/borg`` main repository to your own Github repository. Then clone your Github repository to your local machine. The instructions for forking and cloning a repository can be found there: ``_ . To work on your contribution, you first need to decide which branch your pull request should be against. Often, this might be master branch (esp. for big / risky contributions), but it could be also a maintenance branch like e.g. 1.4-maint (esp. for small fixes that should go into next maintenance release, e.g. 1.4.x). Start by checking out the appropriate branch: :: git checkout master It is best practice for a developer to keep local ``master`` branch as an uptodate copy of the upstream ``master`` branch and always do own work in a separate feature or bugfix branch. This is useful to be able to rebase own branches onto the upstream branches they were branched from, if necessary. This also applies to other upstream branches (like e.g. ``1.4-maint``), not only to ``master``. Thus, create a new branch now: :: git checkout -b MYCONTRIB-master # choose an appropriate own branch name Now, work on your contribution in that branch. Use these git commands: :: git status # is there anything that needs to be added? git add ... # if so, add it git commit # finally, commit it. use a descriptive comment. Then push the changes to your Github repository: :: git push --set-upstream origin MYCONTRIB-master Finally, make a pull request on ``borgbackup/borg`` Github repository against the appropriate branch (e.g. ``master``) so that your changes can be reviewed. What to do if work was accidentally started in wrong branch ----------------------------------------------------------- If you accidentally worked in ``master`` branch, check out the ``master`` branch and make sure there are no uncommitted changes. Then, create a feature branch from that, so that your contribution is in a feature branch. :: git checkout master git checkout -b MYCONTRIB-master Next, check out the ``master`` branch again. Find the commit hash of the last commit that was made before you started working on your contribution and perform a hard reset. :: git checkout master git log git reset --hard THATHASH Then, update the local ``master`` branch with changes made in the upstream repository. :: git pull borg master Rebase feature branch onto updated master branch ------------------------------------------------ After updating the local ``master`` branch from upstream, the feature branch can be checked out and rebased onto (the now uptodate) ``master`` branch. :: git checkout MYCONTRIB-master git rebase -i master Next, check if there are any commits that exist in the feature branch but not in the ``master`` branch and vice versa. If there are no conflicts or after resolving them, push your changes to your Github repository. :: git log git diff master git push -f Code and issues --------------- Code is stored on GitHub, in the `Borgbackup organization `_. `Issues `_ and `pull requests `_ should be sent there as well. See also the :ref:`support` section for more details. Style guide / Automated Code Formatting --------------------------------------- We use `black`_ for automatically formatting the code. If you work on the code, it is recommended that you run black **before each commit** (so that new code is always using the desired formatting and no additional commits are required to fix the formatting). :: pip install -r requirements.d/codestyle.txt # everybody use same black version black --check . # only check, don't change black . # reformat the code The CI workflows will check the code formatting and will fail if it is not formatted correctly. When (mass-)reformatting existing code, we need to avoid ruining `git blame`, so please follow their `guide about avoiding ruining git blame`_: .. _black: https://black.readthedocs.io/ .. _guide about avoiding ruining git blame: https://black.readthedocs.io/en/stable/guides/introducing_black_to_your_project.html#avoiding-ruining-git-blame Continuous Integration ---------------------- All pull requests go through `GitHub Actions`_, which runs the tests on misc. Python versions and on misc. platforms as well as some additional checks. .. _GitHub Actions: https://github.com/borgbackup/borg/actions Output and Logging ------------------ When writing logger calls, always use correct log level (debug only for debugging, info for informative messages, warning for warnings, error for errors, critical for critical errors/states). When directly talking to the user (e.g. Y/N questions), do not use logging, but directly output to stderr (not: stdout, it could be connected to a pipe). To control the amount and kinds of messages output emitted at info level, use flags like ``--stats`` or ``--list``, then create a topic logger for messages controlled by that flag. See ``_setup_implied_logging()`` in ``borg/archiver.py`` for the entry point to topic logging. Building a development environment ---------------------------------- First, just install borg into a virtual env :ref:`as described before `. To install some additional packages needed for running the tests, activate your virtual env and run:: pip install -r requirements.d/development.txt This project utilizes pre-commit to format and lint code before it is committed. Although pre-commit is installed when running the command above, the pre-commit hooks will have to be installed separately. Run this command to install the pre-commit hooks:: pre-commit install Running the tests ----------------- The tests are in the borg/testsuite package. To run all the tests, you need to have fakeroot installed. If you do not have fakeroot, you still will be able to run most tests, just leave away the `fakeroot -u` from the given command lines. To run the test suite use the following command:: fakeroot -u tox # run all tests Some more advanced examples:: # verify a changed tox.ini (run this after any change to tox.ini): fakeroot -u tox --recreate fakeroot -u tox -e py39 # run all tests, but only on python 3.9 fakeroot -u tox borg.testsuite.locking # only run 1 test module fakeroot -u tox borg.testsuite.locking -- -k '"not Timer"' # exclude some tests fakeroot -u tox borg.testsuite -- -v # verbose py.test Important notes: - When using ``--`` to give options to py.test, you MUST also give ``borg.testsuite[.module]``. Running the tests (using the pypi package) ------------------------------------------ Since borg 1.4, it is also possible to run the tests without a development environment, using the borgbackup dist package (downloaded from pypi.org or github releases page):: # optional: create and use a virtual env: python3 -m venv env . env/bin/activate # install packages pip install borgbackup pip install pytest pytest-benchmark # run the tests pytest -v -rs --benchmark-skip --pyargs borg.testsuite Adding a compression algorithm ------------------------------ If you want to add a new compression algorithm, please refer to :issue:`1633` and leave a post there in order to discuss about the proposal. Documentation ------------- Generated files ~~~~~~~~~~~~~~~ Usage documentation (found in ``docs/usage/``) and man pages (``docs/man/``) are generated automatically from the command line parsers declared in the program and their documentation, which is embedded in the program (see archiver.py). These are committed to git for easier use by packagers downstream. When a command is added, a command line flag changed, added or removed, the usage docs need to be rebuilt as well:: python scripts/make.py build_usage python scripts/make.py build_man However, we prefer to do this as part of our :ref:`releasing` preparations, so it is generally not necessary to update these when submitting patches that change something about the command line. Building the docs with Sphinx ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The documentation (in reStructuredText format, .rst) is in docs/. To build the html version of it, you need to have Sphinx installed (in your Borg virtualenv with Python 3):: pip install -r requirements.d/docs.txt Now run:: cd docs/ make html Then point a web browser at docs/_build/html/index.html. The website is updated automatically by ReadTheDocs through GitHub web hooks on the main repository. Using Vagrant ------------- We use Vagrant for the automated creation of testing environments and borgbackup standalone binaries for various platforms. For better security, there is no automatic sync in the VM to host direction. The plugin `vagrant-scp` is useful to copy stuff from the VMs to the host. The "windows10" box requires the `reload` plugin (``vagrant plugin install vagrant-reload``). Usage:: # To create and provision the VM: vagrant up OS # same, but use 6 VM cpus and 12 workers for pytest: VMCPUS=6 XDISTN=12 vagrant up OS # To create an ssh session to the VM: vagrant ssh OS # To execute a command via ssh in the VM: vagrant ssh OS -c "command args" # To shut down the VM: vagrant halt OS # To shut down and destroy the VM: vagrant destroy OS # To copy files from the VM (in this case, the generated binary): vagrant scp OS:/vagrant/borg/borg.exe . Creating standalone binaries ---------------------------- Make sure you have everything built and installed (including fuse stuff). When using the Vagrant VMs, pyinstaller will already be installed. With virtual env activated:: pip install pyinstaller # or git checkout master pyinstaller -F -n borg-PLATFORM borg/__main__.py for file in dist/borg-*; do gpg --armor --detach-sign $file; done If you encounter issues, see also our `Vagrantfile` for details. .. note:: Standalone binaries built with pyinstaller are supposed to work on same OS, same architecture (x86 32bit, amd64 64bit) without external dependencies. .. _releasing: Creating a new release ---------------------- Checklist: - Make sure all issues for this milestone are closed or moved to the next milestone. - Check if there are any pending fixes for security issues. - Find and fix any low hanging fruit left on the issue tracker. - Check that GitHub Actions CI is happy. - Update ``CHANGES.rst``, based on ``git log $PREVIOUS_RELEASE..``. - Check version number of upcoming release in ``CHANGES.rst``. - Render ``CHANGES.rst`` via ``make html`` and check for markup errors. - Verify that ``MANIFEST.in``, ``pyproject.toml`` and ``setup.py`` are complete. - Run these commands and commit:: python scripts/make.py build_usage python scripts/make.py build_man - Tag the release:: git tag -s -m "tagged/signed release X.Y.Z" X.Y.Z - Create a clean repo and use it for the following steps:: git clone borg borg-clean This makes sure no uncommitted files get into the release archive. It will also reveal uncommitted required files. Moreover, it makes sure the vagrant machines only get committed files and do a fresh start based on that. - Run tox and/or binary builds on all supported platforms via vagrant, check for test failures. - Create sdist, sign it, upload release to (test) PyPi: :: scripts/sdist-sign X.Y.Z scripts/upload-pypi X.Y.Z test scripts/upload-pypi X.Y.Z Note: the signature is not uploaded to PyPi any more, but we upload it to github releases. - Put binaries into dist/borg-OSNAME and sign them: :: scripts/sign-binaries 201912312359 - Close the release milestone on GitHub. - `Update borgbackup.org `_ with the new version number and release date. - Announce on: - Mailing list. - Twitter. - IRC channel (change ``/topic``). - Create a GitHub release, include: * pypi dist package and signature * Standalone binaries (see above for how to create them). + For macOS, document the macFUSE version in the README of the binaries. macFUSE uses a kernel extension that needs to be compatible with the code contained in the binary. * A link to ``CHANGES.rst``. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/faq.rst0000644000076500000240000013414114716225054014757 0ustar00twstaff.. include:: global.rst.inc .. highlight:: none .. _faq: Frequently asked questions ========================== Usage & Limitations ################### What is the difference between a repo on an external hard drive vs. repo on a server? ------------------------------------------------------------------------------------- If Borg is running in client/server mode, the client uses SSH as a transport to talk to the remote agent, which is another Borg process (Borg is installed on the server, too) started automatically by the client. The Borg server is doing storage-related low-level repo operations (list, load and store objects), while the Borg client does the high-level stuff: deduplication, encryption, compression, dealing with archives, backups, restores, etc., which reduces the amount of data that goes over the network. When Borg is writing to a repo on a locally mounted remote file system, e.g. SSHFS, the Borg client only can do file system operations and has no agent running on the remote side, so *every* operation needs to go over the network, which is slower. Can I back up from multiple servers into a single repository? ------------------------------------------------------------- Yes, you can! Even simultaneously. Can I back up to multiple, swapped backup targets? -------------------------------------------------- It is possible to swap your backup disks if each backup medium is assigned its own repository by creating a new one with :ref:`borg_repo-create`. Can I copy or synchronize my repo to another location? ------------------------------------------------------ If you want to have redundant backup repositories (preferably at separate locations), the recommended way to do that is like this: - ``borg repo-create repo1 --encryption=X`` - ``borg repo-create repo2 --encryption=X --other-repo=repo1`` - maybe do a snapshot to have stable and same input data for both borg create. - client machine ---borg create---> repo1 - client machine ---borg create---> repo2 This will create distinct (different repo ID), but related repositories. Related means using the same chunker secret and the same id_key, thus producing the same chunks / the same chunk ids if the input data is the same. The 2 independent borg create invocations mean that there is no error propagation from repo1 to repo2 when done like that. An alternative way would be to use ``borg transfer`` to copy backup archives from repo1 to repo2. Likely a bit more efficient and the archives would be identical, but suffering from potential error propagation. Warning: using borg with multiple repositories with identical repository ID (like when creating 1:1 repository copies) is not supported and can lead to all sorts of issues, like e.g. cache coherency issues, malfunction, data corruption. "this is either an attack or unsafe" warning -------------------------------------------- About the warning: Cache, or information obtained from the security directory is newer than repository - this is either an attack or unsafe (multiple repos with same ID) "unsafe": If not following the advice from the previous section, you can easily run into this by yourself by restoring an older copy of your repository. "attack": maybe an attacker has replaced your repo by an older copy, trying to trick you into AES counter reuse, trying to break your repo encryption. Borg users have also reported that fs issues (like hw issues / I/O errors causing the fs to become read-only) can cause this warning, see :issue:`7853`. If you decide to ignore this and accept unsafe operation for this repository, you could delete the manifest-timestamp and the local cache: :: borg config id # shows the REPO_ID rm ~/.config/borg/security/REPO_ID/manifest-timestamp borg repo-delete --cache-only This is an unsafe and unsupported way to use borg, you have been warned. Which file types, attributes, etc. are *not* preserved? ------------------------------------------------------- * UNIX domain sockets (because it does not make sense - they are meaningless without the running process that created them and the process needs to recreate them in any case). So, don't panic if your backup misses a UDS! * The precise on-disk (or rather: not-on-disk) representation of the holes in a sparse file. Archive creation has no special support for sparse files, holes are backed up as (deduplicated and compressed) runs of zero bytes. Archive extraction has optional support to extract all-zero chunks as holes in a sparse file. * Some filesystem specific attributes, like btrfs NOCOW, see :ref:`platforms`. Are there other known limitations? ---------------------------------- - borg extract supports restoring only into an empty destination. After extraction, the destination will have exactly the contents of the extracted archive. If you extract into a non-empty destination, borg will (for example) not remove files which are in the destination, but not in the archive. See :issue:`4598` for a workaround and more details. .. _interrupted_backup: If a backup stops mid-way, does the already-backed-up data stay there? ---------------------------------------------------------------------- Yes, the data transferred into the repo stays there - just avoid running ``borg compact`` before you completed the backup, because that would remove chunks that were already transferred to the repo, but not (yet) referenced by an archive. If a backup was interrupted, you normally do not need to do anything special, just invoke ``borg create`` as you always do. You may use the same archive name as in previous attempt or a different one (e.g. if you always include the current datetime), it does not matter. Borg always does full single-pass backups, so it will start again from the beginning - but it will be much faster, because some of the data was already stored into the repo, so it does not need to get transmitted and stored again. How can I back up huge file(s) over a unstable connection? ---------------------------------------------------------- Yes. For more details, see :ref:`interrupted_backup`. How can I restore huge file(s) over an unstable connection? ----------------------------------------------------------- Try using ``borg mount`` and ``rsync`` (or a similar tool that supports resuming a partial file copy from what's already copied). How can I switch append-only mode on and off? --------------------------------------------- You could do that (via borg config REPO append_only 0/1), but using different ssh keys and different entries in ``authorized_keys`` is much easier and also maybe has less potential of things going wrong somehow. My machine goes to sleep causing `Broken pipe` ---------------------------------------------- While backing up your data over the network, your machine should not go to sleep. On macOS you can use `caffeinate` to avoid that. How can I compare contents of an archive to my local filesystem? ----------------------------------------------------------------- You can instruct ``export-tar`` to send a tar stream to the stdout, and then use ``tar`` to perform the comparison: :: borg export-tar archive-name - | tar --compare -f - -C /path/to/compare/to Can Borg add redundancy to the backup data to deal with hardware malfunction? ----------------------------------------------------------------------------- No, it can't. While that at first sounds like a good idea to defend against some defect HDD sectors or SSD flash blocks, dealing with this in a reliable way needs a lot of low-level storage layout information and control which we do not have (and also can't get, even if we wanted). So, if you need that, consider RAID or a filesystem that offers redundant storage or just make backups to different locations / different hardware. See also :issue:`225`. Can Borg verify data integrity of a backup archive? --------------------------------------------------- Yes, if you want to detect accidental data damage (like bit rot), use the ``check`` operation. It will notice corruption using CRCs and hashes. If you want to be able to detect malicious tampering also, use an encrypted repo. It will then be able to check using CRCs and HMACs. .. _faq-integrityerror: I get an IntegrityError or similar - what now? ---------------------------------------------- A single error does not necessarily indicate bad hardware or a Borg bug. All hardware exhibits a bit error rate (BER). Hard drives are typically specified as exhibiting fewer than one error every 12 to 120 TB (one bit error in 10e14 to 10e15 bits). The specification is often called *unrecoverable read error rate* (URE rate). Apart from these very rare errors there are two main causes of errors: (i) Defective hardware: described below. (ii) Bugs in software (Borg, operating system, libraries): Ensure software is up to date. Check whether the issue is caused by any fixed bugs described in :ref:`important_notes`. .. rubric:: Finding defective hardware .. note:: Hardware diagnostics are operating system dependent and do not apply universally. The commands shown apply for popular Unix-like systems. Refer to your operating system's manual. Checking hard drives Find the drive containing the repository and use *findmnt*, *mount* or *lsblk* to learn the device path (typically */dev/...*) of the drive. Then, smartmontools can retrieve self-diagnostics of the drive in question:: # smartctl -a /dev/sdSomething The *Offline_Uncorrectable*, *Current_Pending_Sector* and *Reported_Uncorrect* attributes indicate data corruption. A high *UDMA_CRC_Error_Count* usually indicates a bad cable. I/O errors logged by the system (refer to the system journal or dmesg) can point to issues as well. I/O errors only affecting the file system easily go unnoticed, since they are not reported to applications (e.g. Borg), while these errors can still corrupt data. Drives can corrupt some sectors in one event, while remaining reliable otherwise. Conversely, drives can fail completely with no advance warning. If in doubt, copy all data from the drive in question to another drive -- just in case it fails completely. If any of these are suspicious, a self-test is recommended:: # smartctl -t long /dev/sdSomething Running ``fsck`` if not done already might yield further insights. Checking memory Intermittent issues, such as ``borg check`` finding errors inconsistently between runs, are frequently caused by bad memory. Run memtest86+ (or an equivalent memory tester) to verify that the memory subsystem is operating correctly. Checking processors Processors rarely cause errors. If they do, they are usually overclocked or otherwise operated outside their specifications. We do not recommend to operate hardware outside its specifications for productive use. Tools to verify correct processor operation include Prime95 (mprime), linpack, and the `Intel Processor Diagnostic Tool `_ (applies only to Intel processors). .. rubric:: Repairing a damaged repository With any defective hardware found and replaced, the damage done to the repository needs to be ascertained and fixed. :ref:`borg_check` provides diagnostics and ``--repair`` options for repositories with issues. We recommend to first run without ``--repair`` to assess the situation. If the found issues and proposed repairs seem right, re-run "check" with ``--repair`` enabled. How probable is it to get a hash collision problem? --------------------------------------------------- If you noticed, there are some issues (:issue:`170` (**warning: hell**) and :issue:`4884`) about the probability of a chunk having the same hash as another chunk, making the file corrupted because it grabbed the wrong chunk. This is called the `Birthday Problem `_. There is a lot of probability in here so, I can give you my interpretation of such math but it's honestly better that you read it yourself and grab your own resolution from that. Assuming that all your chunks have a size of :math:`2^{21}` bytes (approximately 2.1 MB) and we have a "perfect" hash algorithm, we can think that the probability of collision would be of :math:`p^2/2^{n+1}` then, using SHA-256 (:math:`n=256`) and for example we have 1000 million chunks (:math:`p=10^9`) (1000 million chunks would be about 2100TB). The probability would be around 0.0000000000000000000000000000000000000000000000000000000000043. A mass-murderer space rock happens about once every 30 million years on average. This leads to a probability of such an event occurring in the next second to about :math:`10^{-15}`. That's **45** orders of magnitude more probable than the SHA-256 collision. Briefly stated, if you find SHA-256 collisions scary then your priorities are wrong. This example was grabbed from `this SO answer `_, it's great honestly. Still, the real question is whether Borg tries not to make this happen? Well... previously it did not check anything until there was a feature added which saves the size of the chunks too, so the size of the chunks is compared to the size that you got with the hash and if the check says there is a mismatch it will raise an exception instead of corrupting the file. This doesn't save us from everything but reduces the chances of corruption. There are other ways of trying to escape this but it would affect performance so much that it wouldn't be worth it and it would contradict Borg's design, so if you don't want this to happen, simply don't use Borg. Why is the time elapsed in the archive stats different from wall clock time? ---------------------------------------------------------------------------- Borg needs to write the time elapsed into the archive metadata before finalizing the archive and saving the files cache. This means when Borg is run with e.g. the ``time`` command, the duration shown in the archive stats may be shorter than the full time the command runs for. How do I configure different prune policies for different directories? ---------------------------------------------------------------------- Say you want to prune ``/var/log`` faster than the rest of ``/``. How do we implement that? The answer is to back up to different archive *series* and then implement different prune policies for the different series. For example, you could have a script that does:: borg create --exclude var/log main / borg create logs /var/log Then you would have two different prune calls with different policies:: borg prune --verbose --list -d 30 main borg prune --verbose --list -d 7 logs This will keep 7 days of logs and 30 days of everything else. How do I remove files from an existing backup? ---------------------------------------------- A file is only removed from a BorgBackup repository if all archives that contain the file are deleted and the corresponding data chunks are removed from the repository. There are two ways how to remove files from a repository. 1. Use :ref:`borg_delete` to remove all archives that contain the files. This will of course delete everything in the archive, not only some files. 2. If you really want to remove only some specific files, you can run the :ref:`borg_recreate` command to rewrite all archives with a different ``--exclude`` pattern. See the examples in the manpage for more information. Finally, run :ref:`borg_compact` to delete the data chunks from the repository. Can I safely change the compression level or algorithm? -------------------------------------------------------- The compression level and algorithm don't affect deduplication. Chunk ID hashes are calculated *before* compression. New compression settings will only be applied to new chunks, not existing chunks. So it's safe to change them. Use ``borg repo-compress`` to efficiently recompress a complete repository. Why is backing up an unmodified FAT filesystem slow on Linux? ------------------------------------------------------------- By default, the files cache used by BorgBackup considers the inode of files. When an inode number changes compared to the last backup, it hashes the file again. The ``vfat`` kernel driver does not produce stable inode numbers by default. One way to achieve stable inode numbering is mounting the filesystem using ``nfs=nostale_ro``. Doing so implies mounting the filesystem read-only. Another option is to not consider inode numbers in the files cache by passing ``--files-cache=ctime,size``. Security ######## .. _home_config_borg: How important is the $HOME/.config/borg directory? -------------------------------------------------- The Borg config directory has content that you should take care of: ``keys`` subdirectory All your borg keyfile keys are stored in this directory. Please note that borg repokey keys are stored inside the repository. You MUST make sure to have an independent backup of these keyfiles, otherwise you cannot access your backups anymore if you lose them. You also MUST keep these files secret; everyone who gains access to your repository and has the corresponding keyfile (and the key passphrase) can extract it. Make sure that only you have access to the Borg config directory. .. _home_data_borg: How important is the $HOME/.local/share/borg directory? ------------------------------------------------------- The Borg data directory has content that you should take care of: ``security`` subdirectory Each directory here represents one Borg repository by its ID and contains the last known status. If a repository's status is different from this information at the beginning of BorgBackup operation, Borg outputs warning messages and asks for confirmation, so make sure you do not lose or manipulate these files. However, apart from those warnings, a loss of these files can be recovered. Make sure that only you have access to the Borg data directory. .. _cache_security: Do I need to take security precautions regarding the cache? ----------------------------------------------------------- The cache contains a lot of metadata information about the files in your repositories and it is not encrypted. However, the assumption is that the cache is being stored on the very same system which also contains the original files which are being backed up. So someone with access to the cache files would also have access the original files anyway. The Internals section contains more details about :ref:`cache`. If you ever need to move the cache to a different location, this can be achieved by using the appropriate :ref:`env_vars`. How can I specify the encryption passphrase programmatically? ------------------------------------------------------------- There are several ways to specify a passphrase without human intervention: Setting ``BORG_PASSPHRASE`` The passphrase can be specified using the ``BORG_PASSPHRASE`` environment variable. This is often the simplest option, but can be insecure if the script that sets it is world-readable. .. _password_env: .. note:: Be careful how you set the environment; using the ``env`` command, a ``system()`` call or using inline shell scripts (e.g. ``BORG_PASSPHRASE=hunter2 borg ...``) might expose the credentials in the process list directly and they will be readable to all users on a system. Using ``export`` in a shell script file should be safe, however, as the environment of a process is `accessible only to that user `_. Using ``BORG_PASSCOMMAND`` with a file of proper permissions Another option is to create a file with a password in it in your home directory and use permissions to keep anyone else from reading it. For example, first create a key:: (umask 0077; head -c 32 /dev/urandom | base64 -w 0 > ~/.borg-passphrase) Then in an automated script one can put:: export BORG_PASSCOMMAND="cat $HOME/.borg-passphrase" and Borg will automatically use that passphrase. Using keyfile-based encryption with a blank passphrase It is possible to encrypt your repository in ``keyfile`` mode instead of the default ``repokey`` mode and use a blank passphrase for the key file (simply press Enter twice when ``borg repo-create`` asks for the password). See :ref:`encrypted_repos` for more details. Using ``BORG_PASSCOMMAND`` with macOS Keychain macOS has a native manager for secrets (such as passphrases) which is safer than just using a file as it is encrypted at rest and unlocked manually (fortunately, the login keyring automatically unlocks when you log in). With the built-in ``security`` command, you can access it from the command line, making it useful for ``BORG_PASSCOMMAND``. First generate a passphrase and use ``security`` to save it to your login (default) keychain:: security add-generic-password -D secret -U -a $USER -s borg-passphrase -w $(head -c 32 /dev/urandom | base64 -w 0) In your backup script retrieve it in the ``BORG_PASSCOMMAND``:: export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w" Using ``BORG_PASSCOMMAND`` with GNOME Keyring GNOME also has a keyring daemon that can be used to store a Borg passphrase. First ensure ``libsecret-tools``, ``gnome-keyring`` and ``libpam-gnome-keyring`` are installed. If ``libpam-gnome-keyring`` wasn't already installed, ensure it runs on login:: sudo sh -c "echo session optional pam_gnome_keyring.so auto_start >> /etc/pam.d/login" sudo sh -c "echo password optional pam_gnome_keyring.so >> /etc/pam.d/passwd" # you may need to relogin afterwards to activate the login keyring Then add a secret to the login keyring:: head -c 32 /dev/urandom | base64 -w 0 | secret-tool store borg-repository repo-name --label="Borg Passphrase" If a dialog box pops up prompting you to pick a password for a new keychain, use your login password. If there is a checkbox for automatically unlocking on login, check it to allow backups without any user intervention whatsoever. Once the secret is saved, retrieve it in a backup script using ``BORG_PASSCOMMAND``:: export BORG_PASSCOMMAND="secret-tool lookup borg-repository repo-name" .. note:: For this to unlock the keychain automatically it must be run in the ``dbus`` session of an unlocked terminal; for example, running a backup script as a ``cron`` job might not work unless you also ``export DISPLAY=:0`` so ``secret-tool`` can pick up your open session. `It gets even more complicated`__ when you are running the tool as a different user (e.g. running a backup as root with the password stored in the user keyring). __ https://github.com/borgbackup/borg/pull/2837#discussion_r127641330 Using ``BORG_PASSCOMMAND`` with KWallet KDE also has a keychain feature in the form of KWallet. The command-line tool ``kwalletcli`` can be used to store and retrieve secrets. Ensure ``kwalletcli`` is installed, generate a passphrase, and store it in your "wallet":: head -c 32 /dev/urandom | base64 -w 0 | kwalletcli -Pe borg-passphrase -f Passwords Once the secret is saved, retrieve it in a backup script using ``BORG_PASSCOMMAND``:: export BORG_PASSCOMMAND="kwalletcli -e borg-passphrase -f Passwords" When backing up to remote encrypted repos, is encryption done locally? ---------------------------------------------------------------------- Yes, file and directory metadata and data is locally encrypted, before leaving the local machine. We do not mean the transport layer encryption by that, but the data/metadata itself. Transport layer encryption (e.g. when ssh is used as a transport) applies additionally. When backing up to remote servers, do I have to trust the remote server? ------------------------------------------------------------------------ Yes and No. No, as far as data confidentiality is concerned - if you use encryption, all your files/dirs data and metadata are stored in their encrypted form into the repository. Yes, as an attacker with access to the remote server could delete (or otherwise make unavailable) all your backups. How can I protect against a hacked backup client? ------------------------------------------------- Assume you back up your backup client machine C to the backup server S and C gets hacked. In a simple push setup, the attacker could then use borg on C to delete all backups residing on S. These are your options to protect against that: - Do not allow to delete data permanently from the repo, see :ref:`append_only_mode`. - Use a pull-mode setup using ``ssh -R``, see :ref:`pull_backup` for more information. - Mount C's filesystem on another machine and then create a backup of it. - Do not give C filesystem-level access to S. See :ref:`hosting_repositories` for a detailed protection guide. How can I protect against a hacked backup server? ------------------------------------------------- Just in case you got the impression that pull-mode backups are way more safe than push-mode, you also need to consider the case that your backup server S gets hacked. In case S has access to a lot of clients C, that might bring you into even bigger trouble than a hacked backup client in the previous FAQ entry. These are your options to protect against that: - Use the standard push-mode setup (see also previous FAQ entry). - Mount (the repo part of) S's filesystem on C. - Do not give S file-system level access to C. - Have your backup server at a well protected place (maybe not reachable from the internet), configure it safely, apply security updates, monitor it, ... How can I protect against theft, sabotage, lightning, fire, ...? ---------------------------------------------------------------- In general: if your only backup medium is nearby the backupped machine and always connected, you can easily get into trouble: they likely share the same fate if something goes really wrong. Thus: - have multiple backup media - have media disconnected from network, power, computer - have media at another place - have a relatively recent backup on your media How do I report a security issue with Borg? ------------------------------------------- Send a private email to the :ref:`security contact ` if you think you have discovered a security issue. Please disclose security issues responsibly. Common issues ############# /path/to/repo is not a valid repository. Check repo config. ----------------------------------------------------------- There can be many causes of this error. E.g. you have incorrectly specified the repository path. You will also get this error if you try to access a repository with a key that uses the argon2 key algorithm using an old version of borg. We recommend upgrading to the latest stable version and trying again. We are sorry. We should have thought about forward compatibility and implemented a more helpful error message. Why am I seeing idle borg serve processes on the repo server? ------------------------------------------------------------- Please see the next question. Why does Borg disconnect or hang when backing up to a remote server? -------------------------------------------------------------------- Communication with the remote server (using an ssh: repo URL) happens via an SSH connection. This can lead to some issues that would not occur during a local backup: - Since Borg does not send data all the time, the connection may get closed, leading to errors like "connection closed by remote". - On the other hand, network issues may lead to a dysfunctional connection that is only detected after some time by the server, leading to stale ``borg serve`` processes and locked repositories. To fix such problems, please apply these :ref:`SSH settings ` so that keep-alive requests are sent regularly. How can I deal with my very unstable SSH connection? ---------------------------------------------------- If you have issues with lost connections during long-running borg commands, you could try to work around: - Make partial extracts like ``borg extract PATTERN`` to do multiple smaller extraction runs that complete before your connection has issues. - Try using ``borg mount MOUNTPOINT`` and ``rsync -avH`` from ``MOUNTPOINT`` to your desired extraction directory. If the connection breaks down, just repeat that over and over again until rsync does not find anything to do any more. Due to the way borg mount works, this might be less efficient than borg extract for bigger volumes of data. Can I back up my root partition (/) with Borg? ---------------------------------------------- Backing up your entire root partition works just fine, but remember to exclude directories that make no sense to back up, such as /dev, /proc, /sys, /tmp and /run, and to use ``--one-file-system`` if you only want to back up the root partition (and not any mounted devices e.g.). If it crashes with a UnicodeError, what can I do? ------------------------------------------------- Check if your encoding is set correctly. For most POSIX-like systems, try:: export LANG=en_US.UTF-8 # or similar, important is correct charset If that does not help: - check for typos, check if you really used ``export``. - check if you have set ``LC_ALL`` - if so, try not setting it. - check if you generated the respective locale via ``locale-gen``. I can't extract non-ascii filenames by giving them on the commandline!? ----------------------------------------------------------------------- This might be due to different ways to represent some characters in unicode or due to other non-ascii encoding issues. If you run into that, try this: - avoid the non-ascii characters on the commandline by e.g. extracting the parent directory (or even everything) - mount the repo using FUSE and use some file manager .. _expected_performance: What's the expected backup performance? --------------------------------------- Compared to simply copying files (e.g. with ``rsync``), Borg has more work to do. This can make creation of the first archive slower, but saves time and disk space on subsequent runs. Here what Borg does when you run ``borg create``: - Borg chunks the file (using the relatively expensive buzhash algorithm) - It then computes the "id" of the chunk (hmac-sha256 (slow, except if your CPU has sha256 acceleration) or blake2b (fast, in software)) - Then it checks whether this chunk is already in the repo (local hashtable lookup, fast). If so, the processing of the chunk is completed here. Otherwise it needs to process the chunk: - Compresses (the default lz4 is super fast) - Encrypts and authenticates (AES-OCB, usually fast if your CPU has AES acceleration as usual since about 10y, or chacha20-poly1305, fast pure-software crypto) - Transmits to repo. If the repo is remote, this usually involves an SSH connection (does its own encryption / authentication). - Stores the chunk into a key/value store (the key is the chunk id, the value is the data). While doing that, it computes XXH64 of the data (repo low-level checksum, used by borg check --repository). Subsequent backups are usually very fast if most files are unchanged and only a few are new or modified. The high performance on unchanged files primarily depends only on a few factors (like FS recursion + metadata reading performance and the files cache working as expected) and much less on other factors. E.g., for this setup: - server grade machine (4C/8T 2013 Xeon, 64GB RAM, 2x good 7200RPM disks) - local zfs filesystem (mirrored) containing the backup source data - repository is remote (does not matter much for unchanged files) - backup job runs while machine is otherwise idle The observed performance is that Borg can process about **1 million unchanged files (and a few small changed ones) in 4 minutes!** If you are seeing much less than that in similar circumstances, read the next few FAQ entries below. .. _slow_backup: Why is my backup so slow? -------------------------- If you feel your Borg backup is too slow somehow, here is what you can do: - Make sure Borg has enough RAM (depends on how big your repo is / how many files you have) - Use one of the blake2 modes for --encryption except if you positively know your CPU (and openssl) accelerates sha256 (then stay with hmac-sha256). - Don't use any expensive compression. The default is lz4 and super fast. Uncompressed is often slower than lz4. - Just wait. You can also interrupt it and start it again as often as you like, it will converge against a valid "completed" state. It is starting from the beginning each time, but it is still faster then as it does not store data into the repo which it already has there. - If you don’t need additional file attributes, you can disable them with ``--noflags``, ``--noacls``, ``--noxattrs``. This can lead to noticeable performance improvements when your backup consists of many small files. To see what files have changed and take more time processing, you can also add ``--list --filter=AME --stats`` to your ``borg create`` call to produce more log output, including a file list (with file status characters) and also some statistics at the end of the backup. Then you do the backup and look at the log output: - stats: Do you really have little changes or are there more changes than you thought? In the stats you can see the overall volume of changed data, which needed to be added to the repo. If that is a lot, that can be the reason why it is slow. - ``A`` status ("added") in the file list: If you see that often, you have a lot of new files (files that Borg did not find in the files cache). If you think there is something wrong with that (the file was there already in the previous backup), please read the FAQ entries below. - ``M`` status ("modified") in the file list: If you see that often, Borg thinks that a lot of your files might be modified (Borg found them in the files cache, but the metadata read from the filesystem did not match the metadata stored in the files cache). In such a case, Borg will need to process the files' contents completely, which is much slower than processing unmodified files (Borg does not read their contents!). The metadata values used in this comparison are determined by the ``--files-cache`` option and could be e.g. size, ctime and inode number (see the ``borg create`` docs for more details and potential issues). You can use the ``stat`` command on files to look at fs metadata manually to debug if there is any unexpected change triggering the ``M`` status. Also, the ``--debug-topic=files_cache`` option of ``borg create`` provides a lot of debug output helping to analyse why the files cache does not give its expected high performance. When borg runs inside a virtual machine, there are some more things to look at: Some hypervisors (e.g. kvm on proxmox) give some broadly compatible CPU type to the VM (usually to ease migration between VM hosts of potentially different hardware CPUs). It is broadly compatible because they leave away modern CPU features that could be not present in older or other CPUs, e.g. hardware acceleration for AES crypto, for sha2 hashes, for (P)CLMUL(QDQ) computations useful for crc32. So, basically you pay for compatibility with bad performance. If you prefer better performance, you should try to expose the host CPU's misc. hw acceleration features to the VM which runs borg. On Linux, check ``/proc/cpuinfo`` for the CPU flags inside the VM. For kvm check the docs about "Host model" and "Host passthrough". See also the next few FAQ entries for more details. .. _a_status_oddity: I am seeing 'A' (added) status for an unchanged file!? ------------------------------------------------------ The files cache is used to determine whether Borg already "knows" / has backed up a file and if so, to skip the file from chunking. It intentionally *excludes* files that have a timestamp which is the same as the newest timestamp in the created archive. So, if you see an 'A' status for unchanged file(s), they are likely the files with the most recent timestamp in that archive. This is expected: it is to avoid data loss with files that are backed up from a snapshot and that are immediately changed after the snapshot (but within timestamp granularity time, so the timestamp would not change). Without the code that removes these files from the files cache, the change that happened right after the snapshot would not be contained in the next backup as Borg would think the file is unchanged. This does not affect deduplication, the file will be chunked, but as the chunks will often be the same and already stored in the repo (except in the above mentioned rare condition), it will just re-use them as usual and not store new data chunks. If you want to avoid unnecessary chunking, just create or touch a small or empty file in your backup source file set (so that one has the latest timestamp, not your 50GB VM disk image) and, if you do snapshots, do the snapshot after that. Since only the files cache is used in the display of files status, those files are reported as being added when, really, chunks are already used. By default, ctime (change time) is used for the timestamps to have a rather safe change detection (see also the --files-cache option). Furthermore, pathnames used as key into the files cache are **as archived**, so make sure these are always the same (see ``borg list``). .. _always_chunking: It always chunks all my files, even unchanged ones! --------------------------------------------------- Borg maintains a files cache where it remembers the timestamps, size and inode of files. When Borg does a new backup and starts processing a file, it first looks whether the file has changed (compared to the values stored in the files cache). If the values are the same, the file is assumed unchanged and thus its contents won't get chunked (again). The files cache is stored separately (using a different filename suffix) per archive series, thus using always the same name for the archive is strongly recommended. The "rebuild files cache from previous archive in repo" feature also depends on that. Alternatively, there is also BORG_FILES_CACHE_SUFFIX which can be used to manually set a custom suffix (if you can't just use the same archive name). Another possible reason is that files don't always have the same path - borg uses the paths as seen in the archive when using ``borg list``. It is possible for some filesystems, such as ``mergerfs`` or network filesystems, to return inconsistent inode numbers across runs, causing borg to consider them changed. A workaround is to set the option ``--files-cache=ctime,size`` to exclude the inode number comparison from the files cache check so that files with different inode numbers won't be treated as modified. Is there a way to limit bandwidth with Borg? -------------------------------------------- To limit upload (i.e. :ref:`borg_create`) bandwidth, use the ``--remote-ratelimit`` option. There is no built-in way to limit *download* (i.e. :ref:`borg_extract`) bandwidth, but limiting download bandwidth can be accomplished with pipeviewer_: Create a wrapper script: /usr/local/bin/pv-wrapper :: #!/bin/sh ## -q, --quiet do not output any transfer information at all ## -L, --rate-limit RATE limit transfer to RATE bytes per second RATE=307200 pv -q -L $RATE | "$@" Add BORG_RSH environment variable to use pipeviewer wrapper script with ssh. :: export BORG_RSH='/usr/local/bin/pv-wrapper ssh' Now Borg will be bandwidth limited. The nice thing about ``pv`` is that you can change rate-limit on the fly: :: pv -R $(pidof pv) -L 102400 .. _pipeviewer: http://www.ivarch.com/programs/pv.shtml How can I avoid unwanted base directories getting stored into archives? ----------------------------------------------------------------------- Possible use cases: - Another file system is mounted and you want to back it up with original paths. - You have created a BTRFS snapshot in a ``/.snapshots`` directory for backup. To achieve this, run ``borg create`` within the mountpoint/snapshot directory: :: # Example: Some file system mounted in /mnt/rootfs. cd /mnt/rootfs borg create rootfs_backup . Another way (without changing the directory) is to use the slashdot hack: :: borg create rootfs_backup /mnt/rootfs/./ I am having troubles with some network/FUSE/special filesystem, why? -------------------------------------------------------------------- Borg is doing nothing special in the filesystem, it only uses very common and compatible operations (even the locking is just "rename"). So, if you are encountering issues like slowness, corruption or malfunction when using a specific filesystem, please try if you can reproduce the issues with a local (non-network) and proven filesystem (like ext4 on Linux). If you can't reproduce the issue then, you maybe have found an issue within the filesystem code you used (not with Borg). For this case, it is recommended that you talk to the developers / support of the network fs and maybe open an issue in their issue tracker. Do not file an issue in the Borg issue tracker. If you can reproduce the issue with the proven filesystem, please file an issue in the Borg issue tracker about that. Why does running 'borg check --repair' warn about data loss? ------------------------------------------------------------ Repair usually works for recovering data in a corrupted archive. However, it's impossible to predict all modes of corruption. In some very rare instances, such as malfunctioning storage hardware, additional repo corruption may occur. If you can't afford to lose the repo, it's strongly recommended that you perform repair on a copy of the repo. In other words, the warning is there to emphasize that Borg: - Will perform automated routines that modify your backup repository - Might not actually fix the problem you are experiencing - Might, in very rare cases, further corrupt your repository In the case of malfunctioning hardware, such as a drive or USB hub corrupting data when read or written, it's best to diagnose and fix the cause of the initial corruption before attempting to repair the repo. If the corruption is caused by a one time event such as a power outage, running `borg check --repair` will fix most problems. Why isn't there more progress / ETA information displayed? ---------------------------------------------------------- Some borg runs take quite a bit, so it would be nice to see a progress display, maybe even including a ETA (expected time of "arrival" [here rather "completion"]). For some functionality, this can be done: if the total amount of work is more or less known, we can display progress. So check if there is a ``--progress`` option. But sometimes, the total amount is unknown (e.g. for ``borg create`` we just do a single pass over the filesystem, so we do not know the total file count or data volume before reaching the end). Adding another pass just to determine that would take additional time and could be incorrect, if the filesystem is changing. Even if the fs does not change and we knew count and size of all files, we still could not compute the ``borg create`` ETA as we do not know the amount of changed chunks, how the bandwidth of source and destination or system performance might fluctuate. You see, trying to display ETA would be futile. The borg developers prefer to rather not implement progress / ETA display than doing futile attempts. See also: https://xkcd.com/612/ Why am I getting 'Operation not permitted' errors when backing up on sshfs? --------------------------------------------------------------------------- By default, ``sshfs`` is not entirely POSIX-compliant when renaming files due to a technicality in the SFTP protocol. Fortunately, it also provides a workaround_ to make it behave correctly:: sshfs -o workaround=rename user@host:dir /mnt/dir .. _workaround: https://unix.stackexchange.com/a/123236 How do I rename a repository? ----------------------------- There is nothing special that needs to be done, you can simply rename the directory that corresponds to the repository. However, the next time borg interacts with the repository (i.e, via ``borg list``), depending on the value of ``BORG_RELOCATED_REPO_ACCESS_IS_OK``, borg may warn you that the repository has been moved. You will be given a prompt to confirm you are OK with this. If ``BORG_RELOCATED_REPO_ACCESS_IS_OK`` is unset, borg will interactively ask for each repository whether it's OK. It may be useful to set ``BORG_RELOCATED_REPO_ACCESS_IS_OK=yes`` to avoid the prompts when renaming multiple repositories or in a non-interactive context such as a script. See :doc:`deployment` for an example. My backup disk is full, what can I do? -------------------------------------- Borg cannot work if you really have zero free space on the backup disk, so the first thing you must do is deleting some files to regain free disk space. See :ref:`about_free_space` for further details. Some Borg commands that do not change the repository might work under disk-full conditions, but generally this should be avoided. If your backup disk is already full when Borg starts a write command like `borg create`, it will abort immediately and the repository will stay as-is. Miscellaneous ############# macOS: borg mounts not shown in Finder's side bar ------------------------------------------------- https://github.com/osxfuse/osxfuse/wiki/Mount-options#local Read the above first and use this on your own risk:: borg mount -olocal REPO MOUNTPOINT Requirements for the borg single-file binary, esp. (g)libc? ----------------------------------------------------------- We try to build the binary on old, but still supported systems - to keep the minimum requirement for the (g)libc low. The (g)libc can't be bundled into the binary as it needs to fit your kernel and OS, but Python and all other required libraries will be bundled into the binary. If your system fulfills the minimum (g)libc requirement (see the README that is released with the binary), there should be no problem. If you are slightly below the required version, maybe just try. Due to the dynamic loading (or not loading) of some shared libraries, it might still work depending on what libraries are actually loaded and used. In the borg git repository, there is scripts/glibc_check.py that can determine (based on the symbols' versions they want to link to) whether a set of given (Linux) binaries works with a given glibc version. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/global.rst.inc0000644000076500000240000000314514716225054016217 0ustar00twstaff.. highlight:: bash .. |package_dirname| replace:: borgbackup-|version| .. |package_filename| replace:: |package_dirname|.tar.gz .. |package_url| replace:: https://pypi.python.org/packages/source/b/borgbackup/|package_filename| .. |git_url| replace:: https://github.com/borgbackup/borg.git .. _github: https://github.com/borgbackup/borg .. _issue tracker: https://github.com/borgbackup/borg/issues .. _deduplication: https://en.wikipedia.org/wiki/Data_deduplication .. _AES: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard .. _HMAC-SHA256: https://en.wikipedia.org/wiki/HMAC .. _SHA256: https://en.wikipedia.org/wiki/SHA-256 .. _PBKDF2: https://en.wikipedia.org/wiki/PBKDF2 .. _argon2: https://en.wikipedia.org/wiki/Argon2 .. _ACL: https://en.wikipedia.org/wiki/Access_control_list .. _libacl: https://savannah.nongnu.org/projects/acl/ .. _libattr: https://savannah.nongnu.org/projects/attr/ .. _liblz4: https://github.com/Cyan4973/lz4 .. _libzstd: https://github.com/facebook/zstd .. _OpenSSL: https://www.openssl.org/ .. _`Python 3`: https://www.python.org/ .. _Buzhash: https://en.wikipedia.org/wiki/Buzhash .. _msgpack: https://msgpack.org/ .. _`msgpack-python`: https://pypi.python.org/pypi/msgpack-python/ .. _llfuse: https://pypi.python.org/pypi/llfuse/ .. _pyfuse3: https://pypi.python.org/pypi/pyfuse3/ .. _userspace filesystems: https://en.wikipedia.org/wiki/Filesystem_in_Userspace .. _Cython: http://cython.org/ .. _virtualenv: https://pypi.python.org/pypi/virtualenv/ .. _mailing list discussion about internals: http://librelist.com/browser/attic/2014/5/6/questions-and-suggestions-about-inner-working-of-attic> ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/index.rst0000644000076500000240000000055714716225054015322 0ustar00twstaff.. include:: global.rst.inc .. highlight:: none Borg Documentation ================== .. include:: ../README.rst .. when you add an element here, do not forget to add it to book.rst .. toctree:: :maxdepth: 2 installation quickstart usage deployment faq support changes changes_1.x changes_0.x internals development authors ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/installation.rst0000644000076500000240000004261114716225054016711 0ustar00twstaff.. include:: global.rst.inc .. highlight:: bash .. _installation: Installation ============ There are different ways to install Borg: - :ref:`distribution-package` - easy and fast if a package is available from your distribution. - :ref:`pyinstaller-binary` - easy and fast, we provide a ready-to-use binary file that comes bundled with all dependencies. - :ref:`source-install`, either: - :ref:`windows-binary` - builds a binary file for Windows using MSYS2. - :ref:`pip-installation` - installing a source package with pip needs more installation steps and requires all dependencies with development headers and a compiler. - :ref:`git-installation` - for developers and power users who want to have the latest code or use revision control (each release is tagged). .. _distribution-package: Distribution Package -------------------- Some distributions might offer a ready-to-use ``borgbackup`` package which can be installed with the package manager. .. important:: Those packages may not be up to date with the latest Borg releases. Before submitting a bug report, check the package version and compare that to our latest release then review :doc:`changes` to see if the bug has been fixed. Report bugs to the package maintainer rather than directly to Borg if the package is out of date in the distribution. .. keep this list in alphabetical order ============ ============================================= ======= Distribution Source Command ============ ============================================= ======= Alpine Linux `Alpine repository`_ ``apk add borgbackup`` Arch Linux `[extra]`_ ``pacman -S borg`` Debian `Debian packages`_ ``apt install borgbackup`` Gentoo `ebuild`_ ``emerge borgbackup`` GNU Guix `GNU Guix`_ ``guix package --install borg`` Fedora/RHEL `Fedora official repository`_ ``dnf install borgbackup`` FreeBSD `FreeBSD ports`_ ``cd /usr/ports/archivers/py-borgbackup && make install clean`` macOS `Homebrew`_ | ``brew install borgbackup`` (official formula, **no** FUSE support) | **or** | ``brew install --cask macfuse`` (`private Tap`_, FUSE support) | ``brew install borgbackup/tap/borgbackup-fuse`` Mageia `cauldron`_ ``urpmi borgbackup`` NetBSD `pkgsrc`_ ``pkg_add py-borgbackup`` NixOS `.nix file`_ ``nix-env -i borgbackup`` OpenBSD `OpenBSD ports`_ ``pkg_add borgbackup`` OpenIndiana `OpenIndiana hipster repository`_ ``pkg install borg`` openSUSE `openSUSE official repository`_ ``zypper in borgbackup`` Raspbian `Raspbian testing`_ ``apt install borgbackup`` Ubuntu `Ubuntu packages`_, `Ubuntu PPA`_ ``apt install borgbackup`` ============ ============================================= ======= .. _Alpine repository: https://pkgs.alpinelinux.org/packages?name=borgbackup .. _[extra]: https://www.archlinux.org/packages/?name=borg .. _Debian packages: https://packages.debian.org/search?keywords=borgbackup&searchon=names&exact=1&suite=all§ion=all .. _Fedora official repository: https://packages.fedoraproject.org/pkgs/borgbackup/borgbackup/ .. _FreeBSD ports: https://www.freshports.org/archivers/py-borgbackup/ .. _ebuild: https://packages.gentoo.org/packages/app-backup/borgbackup .. _GNU Guix: https://www.gnu.org/software/guix/package-list.html#borg .. _pkgsrc: http://pkgsrc.se/sysutils/py-borgbackup .. _cauldron: http://madb.mageia.org/package/show/application/0/release/cauldron/name/borgbackup .. _.nix file: https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/backup/borgbackup/default.nix .. _OpenBSD ports: https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/sysutils/borgbackup/ .. _OpenIndiana hipster repository: https://pkg.openindiana.org/hipster/en/search.shtml?token=borg&action=Search .. _openSUSE official repository: https://software.opensuse.org/package/borgbackup .. _Homebrew: https://formulae.brew.sh/formula/borgbackup .. _private Tap: https://github.com/borgbackup/homebrew-tap .. _Raspbian testing: https://archive.raspbian.org/raspbian/pool/main/b/borgbackup/ .. _Ubuntu packages: https://launchpad.net/ubuntu/+source/borgbackup .. _Ubuntu PPA: https://launchpad.net/~costamagnagianfranco/+archive/ubuntu/borgbackup Please ask package maintainers to build a package or, if you can package / submit it yourself, please help us with that! See :issue:`105` on github to followup on packaging efforts. **Current status of package in the repositories** .. start-badges |Packaging status| .. |Packaging status| image:: https://repology.org/badge/vertical-allrepos/borgbackup.svg :alt: Packaging status :target: https://repology.org/project/borgbackup/versions .. end-badges .. _pyinstaller-binary: Standalone Binary ----------------- .. note:: Releases are signed with an OpenPGP key, see :ref:`security-contact` for more instructions. Borg x86/x64 amd/intel compatible binaries (generated with `pyinstaller`_) are available on the releases_ page for the following platforms: * **Linux**: glibc >= 2.28 (ok for most supported Linux releases). Older glibc releases are untested and may not work. * **MacOS**: 10.12 or newer (To avoid signing issues download the file via command line **or** remove the ``quarantine`` attribute after downloading: ``$ xattr -dr com.apple.quarantine borg-macosx64.tgz``) * **FreeBSD**: 12.1 (unknown whether it works for older releases) ARM binaries are built by Johann Bauer, see: https://borg.bauerj.eu/ To install such a binary, just drop it into a directory in your ``PATH``, make borg readable and executable for its users and then you can run ``borg``:: sudo cp borg-linux64 /usr/local/bin/borg sudo chown root:root /usr/local/bin/borg sudo chmod 755 /usr/local/bin/borg Optionally you can create a symlink to have ``borgfs`` available, which is an alias for ``borg mount``:: ln -s /usr/local/bin/borg /usr/local/bin/borgfs Note that the binary uses /tmp to unpack Borg with all dependencies. It will fail if /tmp has not enough free space or is mounted with the ``noexec`` option. You can change the temporary directory by setting the ``TEMP`` environment variable before running Borg. If a new version is released, you will have to download it manually and replace the old version using the same steps as shown above. .. _pyinstaller: http://www.pyinstaller.org .. _releases: https://github.com/borgbackup/borg/releases .. _source-install: From Source ----------- .. note:: Some older Linux systems (like RHEL/CentOS 5) and Python interpreter binaries compiled to be able to run on such systems (like Python installed via Anaconda) might miss functions required by Borg. This issue will be detected early and Borg will abort with a fatal error. Dependencies ~~~~~~~~~~~~ To install Borg from a source package (including pip), you have to install the following dependencies first: * `Python 3`_ >= 3.9.0, plus development headers. * Libraries (library plus development headers): - OpenSSL_ >= 1.1.1 (LibreSSL will not work) - libacl_ (which depends on libattr_) - liblz4_ >= 1.7.0 (r129) - libzstd_ >= 1.3.0 - libxxhash >= 0.8.1 (0.8.0 might work also) * pkg-config (cli tool) and pkgconfig python package (borg uses these to discover header and library location - if it can't import pkgconfig and is not pointed to header/library locations via env vars [see setup.py], it will raise a fatal error). **These must be present before invoking setup.py!** * some other Python dependencies, pip will automatically install them for you. * optionally, if you wish to mount an archive as a FUSE filesystem, you need a FUSE implementation for Python: - Either pyfuse3_ (preferably, newer) or llfuse_ (older). See also the BORG_FUSE_IMPL env variable. - See pyproject.toml about the version requirements. 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, which contains installation scripts for a number of operating systems. In the following, the steps needed to install the dependencies are listed for a selection of platforms. If your distribution is not covered by these instructions, try to use your package manager to install the dependencies. On FreeBSD, you may need to get a recent enough OpenSSL version from FreeBSD ports. After you have installed the dependencies, you can proceed with steps outlined under :ref:`pip-installation`. Debian / Ubuntu +++++++++++++++ Install the dependencies with development headers:: sudo apt-get install python3 python3-dev python3-pip python3-virtualenv \ libacl1-dev libacl1 \ libssl-dev \ liblz4-dev libzstd-dev libxxhash-dev \ build-essential \ pkg-config python3-pkgconfig 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 Ubuntu this means your user is not in the ``fuse`` group. Add yourself to that group, log out and log in again. Fedora ++++++ Install the dependencies with development headers:: sudo dnf install python3 python3-devel python3-pip python3-virtualenv \ libacl-devel libacl \ openssl-devel \ lz4-devel libzstd-devel xxhash-devel \ pkgconf python3-pkgconfig sudo dnf install gcc gcc-c++ redhat-rpm-config sudo dnf install fuse-devel fuse # needed for llfuse sudo dnf install fuse3-devel fuse3 # needed for pyfuse3 openSUSE Tumbleweed / Leap ++++++++++++++++++++++++++ Install the dependencies automatically using zypper:: sudo zypper source-install --build-deps-only borgbackup Alternatively, you can enumerate all build dependencies in the command line:: sudo zypper install python3 python3-devel \ libacl-devel openssl-devel xxhash-devel libzstd-devel liblz4-devel \ python3-Cython python3-Sphinx python3-msgpack-python python3-pkgconfig pkgconf \ python3-pytest python3-setuptools python3-setuptools_scm \ python3-sphinx_rtd_theme gcc gcc-c++ sudo zypper install python3-llfuse # llfuse macOS +++++ When installing borgbackup via Homebrew_, the basic dependencies are installed automatically. For FUSE support to mount the backup archives, you need macFUSE, which is available via `github `__, or Homebrew:: brew install --cask macfuse When installing Borg via ``pip``, be sure to install the ``llfuse`` extra, since macFUSE only supports FUSE API v2. Also, since Homebrew won't link the installed ``openssl`` formula, point pkg-config to the correct path:: PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig" pip install borgbackup[llfuse] When working from a borg git repo workdir, you can install dependencies using the Brewfile:: brew install python@3.11 # can be any supported python3 version brew bundle install # install requirements from borg repo's ./Brewfile pip3 install virtualenv pkgconfig Be aware that for all recent macOS releases you must authorize full disk access. It is no longer sufficient to run borg backups as root. If you have not yet granted full disk access, and you run Borg backup from cron, you will see messages such as:: /Users/you/Pictures/Photos Library.photoslibrary: scandir: [Errno 1] Operation not permitted: To fix this problem, you should grant full disk access to cron, and to your Terminal application. More information `can be found here `__. FreeBSD ++++++++ Listed below are packages you will need to install Borg, its dependencies, and commands to make FUSE work for using the mount command. :: pkg install -y python3 pkgconf pkg install openssl pkg install liblz4 zstd xxhash pkg install fusefs-libs # needed for llfuse pkg install -y git python3 -m ensurepip # to install pip for Python3 To use the mount command: echo 'fuse_load="YES"' >> /boot/loader.conf echo 'vfs.usermount=1' >> /etc/sysctl.conf kldload fuse sysctl vfs.usermount=1 .. _windows_deps: Windows +++++++ .. note:: Running under Windows is experimental. .. warning:: This script needs to be run in the UCRT64 environment in MSYS2. Install the dependencies with the provided script:: ./scripts/msys2-install-deps Windows 10's Linux Subsystem ++++++++++++++++++++++++++++ .. note:: Running under Windows 10's Linux Subsystem is experimental and has not been tested much yet. Just follow the Ubuntu Linux installation steps. You can omit the FUSE stuff, it won't work anyway. Cygwin ++++++ .. note:: Running under Cygwin is experimental and has not been tested much yet. Use the Cygwin installer to install the dependencies:: python39 python39-devel python39-pkgconfig python39-setuptools python39-pip python39-wheel python39-virtualenv libssl-devel libxxhash-devel liblz4-devel libzstd-devel binutils gcc-g++ git make openssh Make sure to use a virtual environment to avoid confusions with any Python installed on Windows. .. _windows-binary: Building a binary on Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: This is experimental. .. warning:: This needs to be run in the UCRT64 environment in MSYS2. Ensure to install the dependencies as described within :ref:`Dependencies: Windows `. :: # Needed for setuptools < 70.2.0 to work - https://www.msys2.org/docs/python/#known-issues # export SETUPTOOLS_USE_DISTUTILS=stdlib pip install -e . pyinstaller -y scripts/borg.exe.spec A standalone executable will be created in ``dist/borg.exe``. .. _pip-installation: Using pip ~~~~~~~~~ Virtualenv_ can be used to build and install Borg without affecting the system Python or requiring root access. Using a virtual environment is optional, but recommended except for the most simple use cases. Ensure to install the dependencies as described within :ref:`source-install`. .. note:: If you install into a virtual environment, you need to **activate** it first (``source borg-env/bin/activate``), before running ``borg``. Alternatively, symlink ``borg-env/bin/borg`` into some directory that is in your ``PATH`` so you can run ``borg``. This will use ``pip`` to install the latest release from PyPi:: virtualenv --python=python3 borg-env source borg-env/bin/activate # might be required if your tools are outdated pip install -U pip setuptools wheel # pkgconfig MUST be available before borg is installed! pip install pkgconfig # install Borg + Python dependencies into virtualenv pip install borgbackup # or alternatively (if you want FUSE support): 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 activating your virtual environment:: pip install -U borgbackup # or ... borgbackup[llfuse/pyfuse3] When doing manual pip installation, man pages are not automatically installed. You can run these commands to install the man pages locally:: # get borg from github git clone https://github.com/borgbackup/borg.git borg # Install the files with proper permissions install -D -m 0644 borg/docs/man/borg*.1* $HOME/.local/share/man/man1/borg.1 # Update the man page cache mandb .. _git-installation: Using git ~~~~~~~~~ This uses latest, unreleased development code from git. While we try not to break master, there are no guarantees on anything. Ensure to install the dependencies as described within :ref:`source-install`. :: # get borg from github git clone https://github.com/borgbackup/borg.git # create a virtual environment virtualenv --python=$(which python3) borg-env source borg-env/bin/activate # always before using! # install borg + dependencies into virtualenv cd borg pip install -r requirements.d/development.txt pip install -r requirements.d/docs.txt # optional, to build the docs 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 installed Python versions # requires fakeroot, available through your package manager fakeroot -u tox --skip-missing-interpreters By default the system installation of python will be used. If you need to use a different version of Python you can install this using ``pyenv``: :: ... # create a virtual environment pyenv install 3.9.0 # minimum, preferably use something more recent! pyenv global 3.9.0 pyenv local 3.9.0 virtualenv --python=${pyenv which python} borg-env source borg-env/bin/activate # always before using! ... .. note:: As a developer or power user, you should always use a virtual environment. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.6040826 borgbackup-2.0.0b14/docs/internals/0000755000076500000240000000000014716232130015442 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals/data-structures.rst0000644000076500000240000011751114716225054021343 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _data-structures: Data structures and file formats ================================ This page documents the internal data structures and storage mechanisms of Borg. It is partly based on `mailing list discussion about internals`_ and also on static code analysis. .. todo:: Clarify terms, perhaps create a glossary. ID (client?) vs. key (repository?), chunks (blob of data in repo?) vs. object (blob of data in repo, referred to from another object?), .. _repository: Repository ---------- Borg stores its data in a `Repository`, which is a key-value store and has the following structure: config/ readme simple text object telling that this is a Borg repository id the unique repository ID encoded as hexadecimal number text version the repository version encoded as decimal number text manifest some data about the repository, binary last-key-checked repository check progress (partial checks, full checks' checkpointing), path of last object checked as text space-reserve.N purely random binary data to reserve space, e.g. for disk-full emergencies There is a list of pointers to archive objects in this directory: archives/ 0000... .. ffff... The actual data is stored into a nested directory structure, using the full object ID as name. Each (encrypted and compressed) object is stored separately. data/ 00/ .. ff/ 00/ .. ff/ 0000... .. ffff... keys/ repokey When using encryption in repokey mode, the encrypted, passphrase protected key is stored here as a base64 encoded text. locks/ used by the locking system to manage shared and exclusive locks. Keys ~~~~ Repository object IDs (which are used as key into the key-value store) are byte-strings of fixed length (256bit, 32 bytes), computed like this:: key = id = id_hash(plaintext_data) # plain = not encrypted, not compressed, not obfuscated The id_hash function depends on the :ref:`encryption mode `. As the id / key is used for deduplication, id_hash must be a cryptographically strong hash or MAC. Repository objects ~~~~~~~~~~~~~~~~~~ Each repository object is stored separately, under its ID into data/xx/yy/xxyy... A repo object has a structure like this: * 32bit meta size * 32bit data size * 64bit xxh64(meta) * 64bit xxh64(data) * meta * data The size and xxh64 hashes can be used for server-side corruption checks without needing to decrypt anything (which would require the borg key). The overall size of repository objects varies from very small (a small source file will be stored as a single repo object) to medium (big source files will be cut into medium sized chunks of some MB). Metadata and data are separately encrypted and authenticated (depending on the user's choices). See :ref:`data-encryption` for a graphic outlining the anatomy of the encryption. Repo object metadata ~~~~~~~~~~~~~~~~~~~~ Metadata is a msgpacked (and encrypted/authenticated) dict with: - ctype (compression type 0..255) - clevel (compression level 0..255) - csize (overall compressed (and maybe obfuscated) data size) - psize (only when obfuscated: payload size without the obfuscation trailer) - size (uncompressed size of the data) Having this separately encrypted metadata makes it more efficient to query the metadata without having to read, transfer and decrypt the (usually much bigger) data part. The compression `ctype` and `clevel` is explained in :ref:`data-compression`. Compaction ~~~~~~~~~~ ``borg compact`` is used to free repository space. It will: - list all object IDs present in the repository - read all archives and determine which object IDs are in use - remove all unused objects from the repository - inform / warn about anything remarkable it found: - warn about IDs used, but not present (data loss!) - inform about IDs that reappeared that were previously lost - compute statistics about: - compression and deduplication factors - repository space usage and space freed The object graph ---------------- On top of the simple key-value store offered by the Repository_, Borg builds a much more sophisticated data structure that is essentially a completely encrypted object graph. Objects, such as archives_, are referenced by their chunk ID, which is cryptographically derived from their contents. More on how this helps security in :ref:`security_structural_auth`. .. figure:: object-graph.png :figwidth: 100% :width: 100% .. _manifest: The manifest ~~~~~~~~~~~~ Compared to borg 1.x: - the manifest moved from object ID 0 to config/manifest - the archives list has been moved from the manifest to archives/* The manifest is rewritten each time an archive is created, deleted, or modified. It looks like this: .. code-block:: python { 'version': 1, 'timestamp': '2017-05-05T12:42:23.042864', 'item_keys': ['acl_access', 'acl_default', ...], 'config': {}, 'archives': { '2017-05-05-system-backup': { 'id': b'<32 byte binary object ID>', 'time': '2017-05-05T12:42:22.942864', }, }, } The *version* field can be either 1 or 2. The versions differ in the way feature flags are handled, described below. The *timestamp* field is used to avoid logical replay attacks where the server just resets the repository to a previous state. *item_keys* is a list containing all Item_ keys that may be encountered in the repository. It is used by *borg check*, which verifies that all keys in all items are a subset of these keys. Thus, an older version of *borg check* supporting this mechanism can correctly detect keys introduced in later versions. *config* is a general-purpose location for additional metadata. All versions of Borg preserve its contents. Feature flags +++++++++++++ Feature flags are used to add features to data structures without causing corruption if older versions are used to access or modify them. The main issues to consider for a feature flag oriented design are flag granularity, flag storage, and cache_ invalidation. Feature flags are divided in approximately three categories, detailed below. Due to the nature of ID-based deduplication, write (i.e. creating archives) and read access are not symmetric; it is possible to create archives referencing chunks that are not readable with the current feature set. The third category are operations that require accurate reference counts, for example archive deletion and check. As the manifest is always updated and always read, it is the ideal place to store feature flags, comparable to the super-block of a file system. The only problem is to recover from a lost manifest, i.e. how is it possible to detect which feature flags are enabled, if there is no manifest to tell. This issue is left open at this time, but is not expected to be a major hurdle; it doesn't have to be handled efficiently, it just needs to be handled. Lastly, cache_ invalidation is handled by noting which feature flags were and which were not understood while manipulating a cache. This allows borg to detect whether the cache needs to be invalidated, i.e. rebuilt from scratch. See `Cache feature flags`_ below. The *config* key stores the feature flags enabled on a repository: .. code-block:: python config = { 'feature_flags': { 'read': { 'mandatory': ['some_feature'], }, 'check': { 'mandatory': ['other_feature'], } 'write': ..., 'delete': ... }, } The top-level distinction for feature flags is the operation the client intends to perform, | the *read* operation includes extraction and listing of archives, | the *write* operation includes creating new archives, | the *delete* (archives) operation, | the *check* operation requires full understanding of everything in the repository. | These are weakly set-ordered; *check* will include everything required for *delete*, *delete* will likely include *write* and *read*. However, *read* may require more features than *write* (due to ID-based deduplication, *write* does not necessarily require reading/understanding repository contents). Each operation can contain several sets of feature flags. Only one set, the *mandatory* set is currently defined. Upon reading the manifest, the Borg client has already determined which operation should be performed. If feature flags are found in the manifest, the set of feature flags supported by the client is compared to the mandatory set found in the manifest. If any unsupported flags are found (i.e. the mandatory set is not a subset of the features supported by the Borg client used), the operation is aborted with a *MandatoryFeatureUnsupported* error: Unsupported repository feature(s) {'some_feature'}. A newer version of borg is required to access this repository. Older Borg releases do not have this concept and do not perform feature flags checks. These can be locked out with manifest version 2. Thus, the only difference between manifest versions 1 and 2 is that the latter is only accepted by Borg releases implementing feature flags. Therefore, as soon as any mandatory feature flag is enabled in a repository, the manifest version must be switched to version 2 in order to lock out all Borg releases unaware of feature flags. .. _Cache feature flags: .. rubric:: Cache feature flags `The cache`_ does not have its separate set of feature flags. Instead, Borg stores which flags were used to create or modify a cache. All mandatory manifest features from all operations are gathered in one set. Then, two sets of features are computed; - those features that are supported by the client and mandated by the manifest are added to the *mandatory_features* set, - the *ignored_features* set comprised of those features mandated by the manifest, but not supported by the client. Because the client previously checked compliance with the mandatory set of features required for the particular operation it is executing, the *mandatory_features* set will contain all necessary features required for using the cache safely. Conversely, the *ignored_features* set contains only those features which were not relevant to operating the cache. Otherwise, the client would not pass the feature set test against the manifest. When opening a cache and the *mandatory_features* set is not a subset of the features supported by the client, the cache is wiped out and rebuilt, since a client not supporting a mandatory feature that the cache was built with would be unable to update it correctly. The assumption behind this behaviour is that any of the unsupported features could have been reflected in the cache and there is no way for the client to discern whether that is the case. Meanwhile, it may not be practical for every feature to have clients using it track whether the feature had an impact on the cache. Therefore, the cache is wiped. When opening a cache and the intersection of *ignored_features* and the features supported by the client contains any elements, i.e. the client possesses features that the previous client did not have and those new features are enabled in the repository, the cache is wiped out and rebuilt. While the former condition likely requires no tweaks, the latter condition is formulated in an especially conservative way to play it safe. It seems likely that specific features might be exempted from the latter condition. .. rubric:: Defined feature flags Currently no feature flags are defined. From currently planned features, some examples follow, these may/may not be implemented and purely serve as examples. - A mandatory *read* feature could be using a different encryption scheme (e.g. session keys). This may not be mandatory for the *write* operation - reading data is not strictly required for creating an archive. - Any additions to the way chunks are referenced (e.g. to support larger archives) would become a mandatory *delete* and *check* feature; *delete* implies knowing correct reference counts, so all object references need to be understood. *check* must discover the entire object graph as well, otherwise the "orphan chunks check" could delete data still in use. .. _archive: Archives ~~~~~~~~ Each archive is an object referenced by an entry below archives/. The archive object itself does not store any of the data contained in the archive it describes. Instead, it contains a list of chunks which form a msgpacked stream of items_. The archive object itself further contains some metadata: * *version* * *name*, which might differ from the name set in the archives/* object. When :ref:`borg_check` rebuilds the manifest (e.g. if it was corrupted) and finds more than one archive object with the same name, it adds a counter to the name in archives/*, but leaves the *name* field of the archives as they were. * *item_ptrs*, a list of "pointer chunk" IDs. Each "pointer chunk" contains a list of chunk IDs of item metadata. * *command_line*, the command line which was used to create the archive * *hostname* * *username* * *time* and *time_end* are the start and end timestamps, respectively * *comment*, a user-specified archive comment * *chunker_params* are the :ref:`chunker-params ` used for creating the archive. This is used by :ref:`borg_recreate` to determine whether a given archive needs rechunking. * Some other pieces of information related to recreate. .. _item: Items ~~~~~ Each item represents a file, directory or other file system item and is stored as a dictionary created by the ``Item`` class that contains: * path * list of data chunks (size: count * ~40B) * user * group * uid * gid * mode (item type + permissions) * source (for symlinks) * hlid (for hardlinks) * rdev (for device files) * mtime, atime, ctime, birthtime in nanoseconds * xattrs * acl (various OS-dependent fields) * flags All items are serialized using msgpack and the resulting byte stream is fed into the same chunker algorithm as used for regular file data and turned into deduplicated chunks. The reference to these chunks is then added to the archive metadata. To achieve a finer granularity on this metadata stream, we use different chunker params for this chunker, which result in smaller chunks. A chunk is stored as an object as well, of course. .. _chunks: .. _chunker_details: Chunks ~~~~~~ Borg has these chunkers: - "fixed": a simple, low cpu overhead, fixed blocksize chunker, optionally supporting a header block of different size. - "buzhash": variable, content-defined blocksize, uses a rolling hash computed by the Buzhash_ algorithm. For some more general usage hints see also ``--chunker-params``. "fixed" chunker +++++++++++++++ The fixed chunker triggers (chunks) at even-spaced offsets, e.g. every 4MiB, producing chunks of same block size (the last chunk is not required to be full-size). Optionally, it supports processing a differently sized "header" first, before it starts to cut chunks of the desired block size. The default is not to have a differently sized header. ``borg create --chunker-params fixed,BLOCK_SIZE[,HEADER_SIZE]`` - BLOCK_SIZE: no default value, multiple of the system page size (usually 4096 bytes) recommended. E.g.: 4194304 would cut 4MiB sized chunks. - HEADER_SIZE: optional, defaults to 0 (no header). The fixed chunker also supports processing sparse files (reading only the ranges with data and seeking over the empty hole ranges). ``borg create --sparse --chunker-params fixed,BLOCK_SIZE[,HEADER_SIZE]`` "buzhash" chunker +++++++++++++++++ The buzhash chunker triggers (chunks) when the last HASH_MASK_BITS bits of the hash are zero, producing chunks with a target size of 2^HASH_MASK_BITS bytes. Buzhash is **only** used for cutting the chunks at places defined by the content, the buzhash value is **not** used as the deduplication criteria (we use a cryptographically strong hash/MAC over the chunk contents for this, the id_hash). The idea of content-defined chunking is assigning every byte where a cut *could* be placed a hash. The hash is based on some number of bytes (the window size) before the byte in question. Chunks are cut where the hash satisfies some condition (usually "n numbers of trailing/leading zeroes"). This causes chunks to be cut in the same location relative to the file's contents, even if bytes are inserted or removed before/after a cut, as long as the bytes within the window stay the same. This results in a high chance that a single cluster of changes to a file will only result in 1-2 new chunks, aiding deduplication. Using normal hash functions this would be extremely slow, requiring hashing approximately ``window size * file size`` bytes. A rolling hash is used instead, which allows to add a new input byte and compute a new hash as well as *remove* a previously added input byte from the computed hash. This makes the cost of computing a hash for each input byte largely independent of the window size. Borg defines minimum and maximum chunk sizes (CHUNK_MIN_EXP and CHUNK_MAX_EXP, respectively) which narrows down where cuts may be made, greatly reducing the amount of data that is actually hashed for content-defined chunking. ``borg create --chunker-params buzhash,CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE`` can be used to tune the chunker parameters, the default is: - CHUNK_MIN_EXP = 19 (minimum chunk size = 2^19 B = 512 kiB) - CHUNK_MAX_EXP = 23 (maximum chunk size = 2^23 B = 8 MiB) - HASH_MASK_BITS = 21 (target chunk size ~= 2^21 B = 2 MiB) - HASH_WINDOW_SIZE = 4095 [B] (`0xFFF`) The buzhash table is altered by XORing it with a seed randomly generated once for the repository, and stored encrypted in the keyfile. This is to prevent chunk size based fingerprinting attacks on your encrypted repo contents (to guess what files you have based on a specific set of chunk sizes). .. _cache: The cache --------- The **files cache** is stored in ``cache/files.`` and is used at backup time to quickly determine whether a given file is unchanged and we have all its chunks. In memory, the files cache is a key -> value mapping (a Python *dict*) and contains: * key: id_hash of the encoded path (same path as seen in archive) * value: - age (0 [newest], ..., BORG_FILES_CACHE_TTL - 1) - file inode number - file size - file ctime_ns - file mtime_ns - list of chunk (id, size) tuples representing the file's contents To determine whether a file has not changed, cached values are looked up via the key in the mapping and compared to the current file attribute values. If the file's size, timestamp and inode number is still the same, it is considered not to have changed. In that case, we check that all file content chunks are (still) present in the repository (we check that via the chunks cache). If everything is matching and all chunks are present, the file is not read / chunked / hashed again (but still a file metadata item is written to the archive, made from fresh file metadata read from the filesystem). This is what makes borg so fast when processing unchanged files. If there is a mismatch or a chunk is missing, the file is read / chunked / hashed. Chunks already present in repo won't be transferred to repo again. The inode number is stored and compared to make sure we distinguish between different files, as a single path may not be unique across different archives in different setups. Not all filesystems have stable inode numbers. If that is the case, borg can be told to ignore the inode number in the check via --files-cache. The age value is used for cache management. If a file is "seen" in a backup run, its age is reset to 0, otherwise its age is incremented by one. If a file was not seen in BORG_FILES_CACHE_TTL backups, its cache entry is removed. The files cache is a python dictionary, storing python objects, which generates a lot of overhead. Borg can also work without using the files cache (saves memory if you have a lot of files or not much RAM free), then all files are assumed to have changed. This is usually much slower than with files cache. The on-disk format of the files cache is a stream of msgpacked tuples (key, value). Loading the files cache involves reading the file, one msgpack object at a time, unpacking it, and msgpacking the value (in an effort to save memory). The **chunks cache** is not persisted to disk, but dynamically built in memory by querying the existing object IDs from the repository. It is used to determine whether we already have a specific chunk. The chunks cache is a key -> value mapping and contains: * key: - chunk id_hash * value: - reference count (always MAX_VALUE as we do not refcount anymore) - size (0 for prev. existing objects, we can't query their plaintext size) The chunks cache is a HashIndex_. .. _cache-memory-usage: Indexes / Caches memory usage ----------------------------- Here is the estimated memory usage of Borg - it's complicated:: chunk_size ~= 2 ^ HASH_MASK_BITS (for buzhash chunker, BLOCK_SIZE for fixed chunker) chunk_count ~= total_file_size / chunk_size chunks_cache_usage = chunk_count * 40 files_cache_usage = total_file_count * 240 + chunk_count * 165 mem_usage ~= chunks_cache_usage + files_cache_usage = chunk_count * 205 + total_file_count * 240 Due to the hashtables, the best/usual/worst cases for memory allocation can be estimated like that:: mem_allocation = mem_usage / load_factor # l_f = 0.25 .. 0.75 mem_allocation_peak = mem_allocation * (1 + growth_factor) # g_f = 1.1 .. 2 All units are Bytes. It is assuming every chunk is referenced exactly once (if you have a lot of duplicate chunks, you will have fewer chunks than estimated above). It is also assuming that typical chunk size is 2^HASH_MASK_BITS (if you have a lot of files smaller than this statistical medium chunk size, you will have more chunks than estimated above, because 1 file is at least 1 chunk). The chunks cache and files cache are all implemented as hash tables. A hash table must have a significant amount of unused entries to be fast - the so-called load factor gives the used/unused elements ratio. When a hash table gets full (load factor getting too high), it needs to be grown (allocate new, bigger hash table, copy all elements over to it, free old hash table) - this will lead to short-time peaks in memory usage each time this happens. Usually does not happen for all hashtables at the same time, though. For small hash tables, we start with a growth factor of 2, which comes down to ~1.1x for big hash tables. E.g. backing up a total count of 1 Mi (IEC binary prefix i.e. 2^20) files with a total size of 1TiB. a) with ``create --chunker-params buzhash,10,23,16,4095`` (custom): mem_usage = 2.8GiB b) with ``create --chunker-params buzhash,19,23,21,4095`` (default): mem_usage = 0.31GiB .. note:: There is also the ``--files-cache=disabled`` option to disable the files cache. You'll save some memory, but it will need to read / chunk all the files as it can not skip unmodified files then. HashIndex --------- The chunks cache is implemented as a hash table, with only one slot per bucket, spreading hash collisions to the following buckets. As a consequence the hash is just a start position for a linear search. If a key is looked up that is not in the table, then the hash table is searched from the start position (the hash) until the first empty bucket is reached. This particular mode of operation is open addressing with linear probing. When the hash table is filled to 75%, its size is grown. When it's emptied to 25%, its size is shrunken. Operations on it have a variable complexity between constant and linear with low factor, and memory overhead varies between 33% and 300%. If an element is deleted, and the slot behind the deleted element is not empty, then the element will leave a tombstone, a bucket marked as deleted. Tombstones are only removed by insertions using the tombstone's bucket, or by resizing the table. They present the same load to the hash table as a real entry, but do not count towards the regular load factor. Thus, if the number of empty slots becomes too low (recall that linear probing for an element not in the index stops at the first empty slot), the hash table is rebuilt. The maximum *effective* load factor, i.e. including tombstones, is 93%. Data in a HashIndex is always stored in little-endian format, which increases efficiency for almost everyone, since basically no one uses big-endian processors any more. HashIndex does not use a hashing function, because all keys (save manifest) are outputs of a cryptographic hash or MAC and thus already have excellent distribution. Thus, HashIndex simply uses the first 32 bits of the key as its "hash". The format is easy to read and write, because the buckets array has the same layout in memory and on disk. Only the header formats differ. The on-disk header is ``struct HashHeader``: - First, the HashIndex magic, the eight byte ASCII string "BORG_IDX". - Second, the signed 32-bit number of entries (i.e. buckets which are not deleted and not empty). - Third, the signed 32-bit number of buckets, i.e. the length of the buckets array contained in the file, and the modulus for index calculation. - Fourth, the signed 8-bit length of keys. - Fifth, the signed 8-bit length of values. This has to be at least four bytes. All fields are packed. The HashIndex is *not* a general purpose data structure. The value size must be at least 4 bytes, and these first bytes are used for in-band signalling in the data structure itself. The constant MAX_VALUE (defined as 2**32-1025 = 4294966271) defines the valid range for these 4 bytes when interpreted as an uint32_t from 0 to MAX_VALUE (inclusive). The following reserved values beyond MAX_VALUE are currently in use (byte order is LE): - 0xffffffff marks empty buckets in the hash table - 0xfffffffe marks deleted buckets in the hash table HashIndex is implemented in C and wrapped with Cython in a class-based interface. The Cython wrapper checks every passed value against these reserved values and raises an AssertionError if they are used. .. _data-encryption: Encryption ---------- .. seealso:: The :ref:`borgcrypto` section for an in-depth review. AEAD modes ~~~~~~~~~~ For new repositories, borg only uses modern AEAD ciphers: AES-OCB or CHACHA20-POLY1305. For each borg invocation, a new sessionkey is derived from the borg key material and the 48bit IV starts from 0 again (both ciphers internally add a 32bit counter to our IV, so we'll just count up by 1 per chunk). The encryption layout is best seen at the bottom of this diagram: .. figure:: encryption-aead.png :figwidth: 100% :width: 100% No special IV/counter management is needed here due to the use of session keys. A 48 bit IV is way more than needed: If you only backed up 4kiB chunks (2^12B), the IV would "limit" the data encrypted in one session to 2^(12+48)B == 2.3 exabytes, meaning you would run against other limitations (RAM, storage, time) way before that. In practice, chunks are usually bigger, for big files even much bigger, giving an even higher limit. Legacy modes ~~~~~~~~~~~~ Old repositories (which used AES-CTR mode) are supported read-only to be able to ``borg transfer`` their archives to new repositories (which use AEAD modes). AES-CTR mode is not supported for new repositories and the related code will be removed in a future release. Both modes ~~~~~~~~~~ Encryption keys (and other secrets) are kept either in a key file on the client ('keyfile' mode) or in the repository under keys/repokey ('repokey' mode). In both cases, the secrets are generated from random and then encrypted by a key derived from your passphrase (this happens on the client before the key is stored into the keyfile or as repokey). The passphrase is passed through the ``BORG_PASSPHRASE`` environment variable or prompted for interactive usage. .. _key_files: Key files --------- .. seealso:: The :ref:`key_encryption` section for an in-depth review of the key encryption. When initializing a repository with one of the "keyfile" encryption modes, Borg creates an associated key file in ``$HOME/.config/borg/keys``. The same key is also used in the "repokey" modes, which store it in the repository. The internal data structure is as follows: version currently always an integer, 2 repository_id the ``id`` field in the ``config`` ``INI`` file of the repository. crypt_key the initial key material used for the AEAD crypto (512 bits) id_key the key used to MAC the plaintext chunk data to compute the chunk's id chunk_seed the seed for the buzhash chunking table (signed 32 bit integer) These fields are packed using msgpack_. The utf-8 encoded passphrase is processed with argon2_ to derive a 256 bit key encryption key (KEK). Then the KEK is used to encrypt and authenticate the packed data using the chacha20-poly1305 AEAD cipher. The result is stored in a another msgpack_ formatted as follows: version currently always an integer, 1 salt random 256 bits salt used to process the passphrase argon2_* some parameters for the argon2 kdf algorithm the algorithms used to process the passphrase (currently the string ``argon2 chacha20-poly1305``) data The encrypted, packed fields. The resulting msgpack_ is then encoded using base64 and written to the key file, wrapped using the standard ``textwrap`` module with a header. The header is a single line with a MAGIC string, a space and a hexadecimal representation of the repository id. .. _data-compression: Compression ----------- Borg supports the following compression methods, each identified by a ctype value in the range between 0 and 255 (and augmented by a clevel 0..255 value for the compression level): - none (no compression, pass through data 1:1), identified by 0x00 - lz4 (low compression, but super fast), identified by 0x01 - zstd (level 1-22 offering a wide range: level 1 is lower compression and high speed, level 22 is higher compression and lower speed) - identified by 0x03 - zlib (level 0-9, level 0 is no compression [but still adding zlib overhead], level 1 is low, level 9 is high compression), identified by 0x05 - lzma (level 0-9, level 0 is low, level 9 is high compression), identified by 0x02. The type byte is followed by a byte indicating the compression level. Speed: none > lz4 > zlib > lzma, lz4 > zstd Compression: lzma > zlib > lz4 > none, zstd > lz4 Be careful, higher compression levels might use a lot of resources (CPU/memory). The overall speed of course also depends on the speed of your target storage. If that is slow, using a higher compression level might yield better overall performance. You need to experiment a bit. Maybe just watch your CPU load, if that is relatively low, increase compression until 1 core is 70-100% loaded. Even if your target storage is rather fast, you might see interesting effects: while doing no compression at all (none) is a operation that takes no time, it likely will need to store more data to the storage compared to using lz4. The time needed to transfer and store the additional data might be much more than if you had used lz4 (which is super fast, but still might compress your data about 2:1). This is assuming your data is compressible (if you back up already compressed data, trying to compress them at backup time is usually pointless). Compression is applied after deduplication, thus using different compression methods in one repo does not influence deduplication. See ``borg create --help`` about how to specify the compression level and its default. Lock files (fslocking) ---------------------- Borg uses filesystem locks to get (exclusive or shared) access to the cache. The locking system is based on renaming a temporary directory to `lock.exclusive` (for exclusive locks). Inside this directory, there is a file indicating hostname, process id and thread id of the lock holder. There is also a json file `lock.roster` that keeps a directory of all shared and exclusive lockers. If the process is able to rename a temporary directory (with the host/process/thread identifier prepared inside it) in the resource directory to `lock.exclusive`, it has the lock for it. If renaming fails (because this directory already exists and its host/process/thread identifier denotes a thread on the host which is still alive), lock acquisition fails. The cache lock is usually in `~/.cache/borg/REPOID/lock.*`. Locks (storelocking) -------------------- To implement locking based on ``borgstore``, borg stores objects below locks/. The objects contain: - a timestamp when lock was created (or refreshed) - host / process / thread information about lock owner - lock type: exclusive or shared Using that information, borg implements: - lock auto-expiry: if a lock is old and has not been refreshed in time, it will be automatically ignored and deleted. the primary purpose of this is to get rid of stale locks by borg processes on other machines. - lock auto-removal if the owner process is dead. the primary purpose of this is to quickly get rid of stale locks by borg processes on the same machine. Breaking the locks ------------------ In case you run into troubles with the locks, you can use the ``borg break-lock`` command after you first have made sure that no Borg process is running on any machine that accesses this resource. Be very careful, the cache or repository might get damaged if multiple processes use it at the same time. If there is an issue just with the repository lock, it will usually resolve automatically (see above), just retry later. Checksumming data structures ---------------------------- As detailed in the previous sections, Borg generates and stores various files containing important meta data, such as the files cache. Data corruption in the files cache could create incorrect archives, e.g. due to wrong object IDs or sizes in the files cache. Therefore, Borg calculates checksums when writing these files and tests checksums when reading them. Checksums are generally 64-bit XXH64 hashes. The canonical xxHash representation is used, i.e. big-endian. Checksums are stored as hexadecimal ASCII strings. For compatibility, checksums are not required and absent checksums do not trigger errors. The mechanisms have been designed to avoid false-positives when various Borg versions are used alternately on the same repositories. Checksums are a data safety mechanism. They are not a security mechanism. .. rubric:: Choice of algorithm XXH64 has been chosen for its high speed on all platforms, which avoids performance degradation in CPU-limited parts (e.g. cache synchronization). Unlike CRC32, it neither requires hardware support (crc32c or CLMUL) nor vectorized code nor large, cache-unfriendly lookup tables to achieve good performance. This simplifies deployment of it considerably (cf. src/borg/algorithms/crc32...). Further, XXH64 is a non-linear hash function and thus has a "more or less" good chance to detect larger burst errors, unlike linear CRCs where the probability of detection decreases with error size. The 64-bit checksum length is considered sufficient for the file sizes typically checksummed (individual files up to a few GB, usually less). xxHash was expressly designed for data blocks of these sizes. Lower layer — file_integrity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There is a lower layer (borg.crypto.file_integrity.IntegrityCheckedFile) wrapping a file-like object, performing streaming calculation and comparison of checksums. Checksum errors are signalled by raising an exception at the earliest possible moment (borg.crypto.file_integrity.FileIntegrityError). .. rubric:: Calculating checksums Before feeding the checksum algorithm any data, the file name (i.e. without any path) is mixed into the checksum, since the name encodes the context of the data for Borg. The various indices used by Borg have separate header and main data parts. IntegrityCheckedFile allows borg to checksum them independently, which avoids even reading the data when the header is corrupted. When a part is signalled, the length of the part name is mixed into the checksum state first (encoded as an ASCII string via `%10d` printf format), then the name of the part is mixed in as an UTF-8 string. Lastly, the current position (length) in the file is mixed in as well. The checksum state is not reset at part boundaries. A final checksum is always calculated in the same way as the parts described above, after seeking to the end of the file. The final checksum cannot prevent code from processing corrupted data during reading, however, it prevents use of the corrupted data. .. rubric:: Serializing checksums All checksums are compiled into a simple JSON structure called *integrity data*: .. code-block:: json { "algorithm": "XXH64", "digests": { "HashHeader": "eab6802590ba39e3", "final": "e2a7f132fc2e8b24" } } The *algorithm* key notes the used algorithm. When reading, integrity data containing an unknown algorithm is not inspected further. The *digests* key contains a mapping of part names to their digests. Integrity data is generally stored by the upper layers, introduced below. An exception is the DetachedIntegrityCheckedFile, which automatically writes and reads it from a ".integrity" file next to the data file. Upper layer ~~~~~~~~~~~ .. rubric:: Main cache files: chunks and files cache The integrity data of the ``files`` cache is stored in the cache ``config``. The ``[integrity]`` section is used: .. code-block:: ini [cache] version = 1 repository = 3c4...e59 manifest = 10e...21c timestamp = 2017-06-01T21:31:39.699514 key_type = 2 previous_location = /path/to/repo [integrity] manifest = 10e...21c files = {"algorithm": "XXH64", "digests": {"HashHeader": "eab...39e3", "final": "e2a...b24"}} The manifest ID is duplicated in the integrity section due to the way all Borg versions handle the config file. Instead of creating a "new" config file from an internal representation containing only the data understood by Borg, the config file is read in entirety (using the Python ConfigParser) and modified. This preserves all sections and values not understood by the Borg version modifying it. Thus, if an older versions uses a cache with integrity data, it would preserve the integrity section and its contents. If a integrity-aware Borg version would read this cache, it would incorrectly report checksum errors, since the older version did not update the checksums. However, by duplicating the manifest ID in the integrity section, it is easy to tell whether the checksums concern the current state of the cache. Integrity errors are fatal in these files, terminating the program, and are not automatically corrected at this time. HardLinkManager and the hlid concept ------------------------------------ Dealing with hard links needs some extra care, implemented in borg within the HardLinkManager class: - At archive creation time, fs items with st_nlink > 1 indicate that they are a member of a group of hardlinks all pointing to the same inode. For such fs items, the archived item includes a hlid attribute (hardlink id), which is computed like H(st_dev, st_ino). Thus, if archived items have the same hlid value, they pointed to the same inode and form a group of hardlinks. Besides that, nothing special is done for any member of the group of hardlinks, meaning that e.g. for regular files, each archived item will have a chunks list. - At extraction time, the presence of a hlid attribute indicates that there might be more hardlinks coming, pointing to the same content (inode), thus borg will remember the "hlid to extracted path" mapping, so it will know the correct path for extracting (hardlinking) the next hardlink of that group / with the same hlid. - This symmetric approach (each item has all the information, e.g. the chunks list) simplifies dealing with such items a lot, especially for partial extraction, for the FUSE filesystem, etc. - This is different from the asymmetric approach of old borg versions (< 2.0) and also from tar which have the concept of a main item (first hardlink, has the content) and content-less secondary items with by-name back references for each subsequent hardlink, causing lots of complications when dealing with them. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals/encryption-aead.odg0000644000076500000240000005427614716225054021244 0ustar00twstaffPKPW.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKPWConfigurations2/toolpanel/PKPWConfigurations2/floater/PKPWConfigurations2/statusbar/PKPWConfigurations2/popupmenu/PKPWConfigurations2/progressbar/PKPWConfigurations2/menubar/PKPWConfigurations2/images/Bitmaps/PKPWConfigurations2/toolbar/PKPWConfigurations2/accelerator/PKPW styles.xml\r8}߯`ijEK8SIefT<DB'!O~ɢq!7ɒ7S[@>}꧇McZ$$WYD$[ >zN?+\&$np܂=px㬘, 4T$n 4V=&Ne7!4P:'M2)FkKP6%$Tc*}eIY,nXrFfDR̓u[vFbˑW-~4g-=sBYt Nkrw+*nhן0jocreutφʾQ N%DeEz 54؀90ҎXIw([9w4Ƞy\O5*ׯ l%)$.ISdxLĈe|B(%|UR*>ʊHXm`] Ѩ+TтV+B\),I"ǜFW [\Iuկtcީ?CϟyJ8)=&cw:-:] p2t$9Wq֦,d\ -)ٵ8M[ WGI;TSWntë*zXym񚛋mcMcVXXo=wMwMhi$wd-m声pC 1 ƨXF]]L_Aڮ aRMk$$(t}1+k+5"Sq)End)ǩ$v4̑P.&QVpYf4UZEM)Z&<t^qzYt'qj4QgG>1 $k^Bó!H;qΈE"4>BE"49Byqzz6F/4NΈˌӐ?D+Pj5mZ7@`PUvܻeE H ;K@Ai (+dҍ#2u44lMvvU^zH$L䆸oA8gmTQd̆Z$rMRK21u%1m3}iHؗWT_-. TЬ~jm PZQJ/V4QR4R4@R7@"l o5F{U ^(:'ad:id.akA AԔVY^DgOAJO1fdvjĔ -I ؚ2avSSg{V5i}t"~j@U;#?B /Dv@NFhb4k~П)[tOi^̛Aftڅ۾R2&W.Sm2FFoP|K2GԪB^\tqщⷺ ]ȔfSݥA]ÓܥI]G~#f}1 WC{N7zȵL_5//Zc;ǏMe/HGC?: 5~FCr5#]=ysaӱ^rlmܿ 0Q/Z@(|S}ܮEE?#>W9-$ bl.З ͻß ׳|=kxDwoaϢ]qb>d'h%'%v"r XqjG_#~I~|O?}cY8OJ,K4oЖo{]ÕbMv6K:;-8h/܈iˀC{{_>z+-ԟ݋@~XTS gWЬЇapAN'Iy)"k&Cf]y>:C%4fၢ(}Qj,_XQkb &|.af~6H:xcǢ4J FO3|吠(acKKokʡ*3xKկoԔ$v>AM&#oaQMŲII,,$tf)XA/g]7;ㅃqS4 G:Ph8}8Ny> ,jU66+\ RHtI`5c8@Sv;4pMVj//W}?PK ̧ UPKPW settings.xmlZ[s8~_a3t$M$ṱB&laTd#q#cJlBmyH@s9ߑs١OXHe谘;aen4lU)1q`+S,g|9͔r˅w1[raJya=A89O2G2Ceeҕ\@rrP: M2xX~W5X&gSb6q࿣Bt5uO6bnB8WY* *-?yc .sr`%zrQ~_3&Ro!0r7z.<$L*[?੊?99J ~G,5B?.~9=M ĞEj_*}? 7O) {]4~PS*HXmc~9ň*Jx8 |Sp)2!z[r\Kٿ+56a2;!w0+bnĦj \JƘd 1biP-ދToph$%aHmPNŤּA ({H/'UPB,ryLF2BYKpsEއrCAd.qߪ>8"s(mz8` [|m52pE[ֹNQb·Y5,[4ӈ fٸϗ,|bXXs?zNKJ1YYyrrXHHQ,ᦾ t-GgN9Z}zV̫:0&0!C2yZLi6Ƥ`=6ȹs"jL(e )~\d% ͪ|f8cL`%f*1UW<ՖLDXE:$zj{''`QMX|'!q& Eߩ0`H;Y},Fd@K^v`F hD;v@-;x%B:`>!D_Nw"d=-?u4H Qswȩb0#4u}rD曣~'ƽ>&էBR'L, .;=ŧPTYHXOrmd0/x8ϐ-Y&rn #ool̵&ER)QP 3ey%j32k^.$wIk̠ e zV\c$=Guqz^*CΙhwfi6IsG34&}ټ ;rT`mo IVD*{Bi[_ cKMR7s?{rۢo7=޷Qʝ>5헩wqkT_6 c6HmϻFst_G];, ڰ~֝;8ʛ[^ݨގfh40Px24g@wѽn7gfih70vZ{{m;p0zOت{,_aHEUZ4J0e$* oiSyT~5{WxoPKp`{c)PKPW8!(-(-Thumbnails/thumbnail.pngPNG  IHDRj[!iPLTE     ! !$" ,*%&'3-;3#$$"%*&.!%)-,,,(,1.3"-277##57$33305;38=;;;5:A9>C>A+;F2>@B[_fqfwl}@@oqw|éC̱FպJMNQSXƈɛĖП›ҤȩֹťČʑЕĂʨα׽þɴܹʃ׋ݪ뙙孵ٺˇӌؐے٩֪ٸՁؔx IDATx|ys|W$Z䑉V q,@ &ENbiGs8"&i-vP?@sf;OnMl67%TUt.w79;SGzywҮĮ}?ywvwއUT؜dw ycVO uLۋau`Y/Ħ>txz i~bwN qĺ 儒{ojE1&mdACV*%d )[<@<,ѕ%3l^%[XyLHP sb#N0)D+E@,z k\،+ .2-HfؖD i*vM(*D5R%#ʽr>!Sm&?W/T&0!CT>k P29| p!U |c26WQHU8dYQi?@g}SXE, zL.s(GEl ˘3[1L! ,?A LhzŖ=7CCldXS! R5$l ECf94c)"xd X4kĬBx),ιrFf>?ʭQC@plǖzj{^/TpXUB7@ԅ֏YpiV `$]&n'PT `}=x*Nq'`Yiȯy5w޸,PlӶ*8{NQ S2G.g,FnBa2%At0*w|>r]&8'ൂ[aH*6J0s jӋ(^&f cg _ί9DV È4Ņl,kLxø1ʳ\ɱA".*4:!r$R$1عb.T `Q<-@8ZC4ljh>%UDC*R+'Q qpEX(m~DT0ڰA!Pcdq(GZ]1`R@p!`>ǰaohk-2yH5JmJ: 3 @)1j0 $=Vh T67\0@@=L :kL"@cه!pX sύB>W 8E\a**H'TN39>( (#1 @1q,Q@Ub*Q<6?-hr%bZ˲jl[a$a I.ȲZ{RW`& 6LjZM/TM1afQ&20bMA"`xe)*K(-Hmߧ"Ah2x$L-Jy!y^\Be&0r1*(hỜ@Kxk3ݛW U-'U@aN&`{, (((#"O`EKDjF> uO<Ͱ 1VwfX %Y WL(K=c}!D bXeM $9(K*0E.R\aEj##3AR>N{:.R3*?Q-29;r`@`x@@@@2 P BA:i;6%}S2Y)V .3{~ѯNw.3K2fոCˡd"qT$ 4᭩fXHtCUP"3k4p QKP KP1MT;T\2Q\^@'T(.  (5x9m@U+@DrU=zfd9wJ}wF6&_[e Z  Kq% oWR`DdxmNp2}6XlYx8 V[w7@"س7DafG7?}ms*>[]ms˝c'qvg[Z ܸG[A3mM9iO73}wK˦%]y+ ?y0yHsQ\mꜫ|O'dhy *I ~x8uv pHX7P7H7GyMo";[_]ۙ7g(@͏G>OP'~s'~> x$s*8q'>?8z1hikіMNkRFenq7lmpڴ#Nkw0z - 77ϽË_=o?OGxz=3n??}k?G\ч~c'~H:xc/e|i  ?8psNkxmmm9[Z0޻һmxtJ7a"( E./C'l&yi)z*NpN\ER"$tN`Lbϙ ~ޠ<@+0 +G93\OD]DDD#eeڬ6Qkוp 4 HUYm`K6mZqH!bɄ$Ewm *HFB { ٢j%>BQZ<9B-+|Ly2m+wrUd*^X蘲U]]<΢#4(Iv9Wk{ d@L \dj(L뚏lFvDZB7@sA_..2aOx +a-2H`z̅(AQ Q2f oDT91E7JDXO<7sw2U]R1@YYϓ*(VbmDD|PdՇYL !D*7RE]N1 E ">|CD/k)(#;ngT+ @(Qj|~} @yGw ޞ|=Cf.nⒿy>۟'/܀e3ht&]ٱY@Eh,(8!jPfdP 0nZDPu+" @ @\QD g, rrޘu> /O;Q+Z_Ҵޑ[^wSKg Q{:ls>oqvA\% P%dkm{޴Ӷm6 @zMtpǝtR1ۣjc@K[:-mlh_afCFg 9ي"DQ*(QQQQY/O_oD.x3_/[{U/}_^^ X @u)g? ںu ܋[_~_~. 0?o|ޏSQ?K k[]/ E|3l7L`u!  ٞJm?Cf/RY$Ivno_@L(sMK>z4pM]l!{O#Ty*W2@r8^c*)a`9P|^j! @ @ @ @ޚF6`NPF @ @ @ @ @ @ @ ᇷ>^|m~3Ťp.&B SͩTH$zQ @F)A2R‘T*S\ Ro.KDNYE BR @#%IŦp$Sl _eyP70ő3fHYx^7L!Y^12]jF03h!srB! \@em#(CQ,hBVL@Jb@P" %xNkĤ'%:攊 IDATXsrLڟ`*0 |ٓRLP1ɸ`<bcy29/. WpTٯ,]l)˻b"! ;?H&*s"Ha-@] . 'Q<'E W F< |d\t9IPO 51J=aTt g *UqC}B@e0B*0 S$T*%JpcG w7NfdH+:+;3@1]at;W|9̌@ @ @(׏g;qU:Sf:pk 1 0{^9{m± Bjy ty ~D( ]T?u к*FR]/%8SJbA{eL:NI.23:l 6@ F F F F F F F F@_|{s.boH Tk-}u-=XIyw,C˖8tvٚGjo~4zv '?l%wozlMדkzfCk?XZhk[ 㑮;wj[nt= w<|s7w=cC>xyĺ-vMV.M'sSW m'w'kB-?xήc7?I絇<ڹv9H`GׁZO]̊y}kŊqgw:+Yon~\T0[3ИV,Z(C v90 c9Z>v`Cp:YK$*]=3J岅!!k`hO9Kr/ _"E>,4nt7 iis-\$ 8Ĵ藻? eI;o'mSX˫s?u;Iti8X_Zv-^mO vm}9- #x' 3M{5EY & `oO`MI}kI[c?@7V)xK $ʞ)@Sj,7hnH ݶ5ͶD/?7zg^~¶@Ću\YdO !ҧ#4:70l[P{xgd嘑oc+b8C-e=fB=i jmpLs9+R(aZ~s]yZ%)mvPMpDc#]( Ҷ1v0et:RP[R4XH<;iƬ/H@Jݼlcbͦr"#ĨGXQۖz`oHB/XǣgQؘV[?6?#PJDګXhh3a^'vAHt JNlm OfIdžLe`0hvc8Id+'MW:a D'}NA[\$턣@R%I Cj4,L C d0["3xlkr _}CN2[~8-tE!EHwWXՏ뗨h~pt+|܏0$iT;6U6d"vŸֲ { [SbIq,T$>9Vnsn>,PZB|YHZ2ˮ."n+GI$4}4U"[ܮN11iRx(YUQʥn{wMxF<$$&! 2&g)!u0 A$$|Ld0 )g%t~etH}^::ʻ\_XFaǮҹ(6gߓhmRqIߧY[xT5rX_ ; JɖRRAV,xOSDJ|)},MH ,>HPmXX C) & &챳`Қ%u B9&Q]:9tS* ouXq\7S}ըFK|`| L(@wx>Q`Y-;:Q4yhve玸qL$Qg75 @ԻFU#4<  uL}&X!_O51{<o?~z*vy9얕4&C6q0eܻ?U{Kjm9۵Uk˓\Q'X,|HH8¤U d]];:P0;<l5M]]5I3`v袚upNE zNEypLF3\[,ı}}8. 5qML` c@ I8Shh|J"(M_^KH4fcc (jHU=ǩD)oQ9e 4а,5VI.: Qui~u z\LdMϞ{R/zIhm\E\:Pu#l.5 )o$mch:jsFJ|.G7Oǟ|/,4EXl~kIrz.3p8?,w1]:M7ݲ1]]TjlWQaO~XTC2'W/y8:N l&tjL^}ICm1/n{w6Bs +2aU.tfuR{aN6z%=U)AR2 f]6WE@{ rP2/A^.nt0WiÍyG~ރ9}޿j) ,o6P.riS*vZڪo) _d'݃PsBsCKA\( ukb8Y PK PKPWmeta.xmlTKo W^m ~#RCTV-xKkl6ůFJ>{ sn@Am@2Ņa$OD0JBu<;Qӈ!\4$i \Xw|~ԣVmGn<&^ւ |OHplbneADt[ox>rM<32>N<8ȦZ1a!>sC mi1WBnſPK*u7%PKPW.++mimetypePKPWQConfigurations2/toolpanel/PKPWConfigurations2/floater/PKPWConfigurations2/statusbar/PKPWConfigurations2/popupmenu/PKPW/Configurations2/progressbar/PKPWiConfigurations2/menubar/PKPWConfigurations2/images/Bitmaps/PKPWConfigurations2/toolbar/PKPWConfigurations2/accelerator/PKPW ̧ U Lstyles.xmlPKPWp`{c) +settings.xmlPKPW8!(-(-Thumbnails/thumbnail.pngPKPW >Ccontent.xmlPKPWsKPmeta.xmlPKPW*u7%SMETA-INF/manifest.xmlPK+}T././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals/encryption-aead.png0000644000076500000240000040245514716225054021253 0ustar00twstaffPNG  IHDR['( pHYsC IDATxw@?E*6EłD#b7Q vEaFƒb[ M`/H+6PQ@DEA1< 2;{w=3  CF@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@F@ j-@FЂt𾱡 U%yIpAúx43w3ƟhykWiN' K]=QՊeU#XcC9X:*;'/۾.~h.4]OyF<cz'dCR\"&PP:vPD։[5| <ҁ'+ j-@F@ j-@<.A뫣ӬY3sss\O,:::((H++u떄tڵJ*oJᡡ t?>7nmР% (}j,U?ڦM*U뫧סCؽ{q>177~1vɓ'dccSPP`dd;A̼zjXX۷ouuu6mjjjڭ[J*)V?֮]p'3܊}Ƙ ]t)cZjZ§>KnHNNRٳg:wm Ƙ.f/yyyʕû,^[^{{˗iF\HMVo߾)))|ZMe__߮]6m<}CN]PPSPB Xzܹ1|5j0ƶl۳gLI0hР*U0z쉫PT/޴i 'N8qġC&MTꯆmڵK}Q|QZ5Ƙ LRVz}:7n|DA "##׭[WV-w,Sرcr\ނcǎi\çC􈎎f۷/111771V|y3uYfO:vڌ1 .]466{Μ9s•C 9"F͛@z@]z$''3ƶl2iҤXll͛/_Xy߾}gΜnР/_X˗;w񉏏jj߾L&:uj{1iҤ+Vxxx}vƍƍu6oU yY[[_tWnܸ7((hΝQQQQQQ٦-Z}\?Sttt7mڔqk׮T\nZ,͛7/33S9CCCw㓐S^=z̙3GM0B 2?Tŋ/^8pQ{_޽[i==;wԩS')))++uڴi ::: ޽r_~dq5ww .`mJβzꘘ\~!^i&3ft-[_ 5y?CrWZմiBҭ[7{N2Ei?1V~J*zJi۷ߺu+66V&խ[z̙+vڵW?tttj׮n:}GGG'&&֫WM6/ƔSN;w.00۷UT111ٳԩ#vUWWW;`ѢEO<}yŊo?ڢE >y$..oרQ%?cN? FFFђcJ^b={ͥ|3f̠j7nÍbŊe˖Gib, 8j(IW^~oEfff ɍ5Ruݻw+J]Fׯ_S͍7CpyUG]vxxrǎc/^Yr$[?0*T`լYSR>b~4KKKLF/VuY֭[Gz ]]]r ݺu wO&MǽpdOyϨxyyQ###C}[n9rDF]b[n،155VYTB(îXb@@}?cǎ933aÆ|ٳ ,<ֹsgGGG(q˓K,a+++nvUmYfQ5'''mip UiٲeVV8Ҫ:Ν;ŕ7o===WN4?]q7uB';Zԍ)ieUcTc䔘(MLLT?/_jժSSSk׮=jԨf͚}1f``0eKKǏٳg͚54&NþիW_xjr<00gΜYbŚ5kx崴mfff27n<}͛yqqq_n׮]\\ kҤɸq6lqΝg򖟥%_%..nرQ#F֭ׯO:ҡCoߪy!_5֭СC6mpؤ; codddؤ:[ncɓgիׇ|.\377aVhcϞ=e2͛7le˖NNNnb3Fr0ԩ!a?/ٳ[n=bĈF0Ƣgм"vچ BCCkժ5n8 ݻwN>GA ܵkg͝8q"]sNj{ GGG=xygϞիo1[[[CCC޾]t "3 ޞo8ֶz!!!۶m 4iҩS4yQڵ㉡M֡CW^|16gjBχիW{1feetn |"1WjiT#ce˖iii+ I޼'NL;wPsuUCxxdpp8k155III3rTHPyAAA׮]5k7o%ڵk|װax?K%.OOO^tT# D]gffRʕ+GtN<)~R;w QT ޿_|WPA.B 1*~ wO }ŀQ%9R(EŻ\]]yb} G5޽{߾}{]4pR>}¡Cjƶm PF Ƙbu2ƚ7o.&՘G!9>{1GS%'rpp'''S }]$M_~S8pgTA +esT#)<<+W?~֭[k+D\rnݺթS{w0`aÆtU3fXg@@ѰalllJ)գm 9++{VVV]bBXPP(YsF[8p@XժU>~~~Orooue2L&3~4H1jI?PcCĨF祤 U*VXsf͚JMMM%M/'K+Vݶo߾ݳg޽ _S˗/LҪ4Ҽ_~ɇa]VGGgs6hkС>gΜz/[L͐F4%C&cQJ=zzGݻw-[TJIԜQɉwٳB|VxJ(OʬR6T4a4iݼy*v̙31vQ-nKPժUjN[b3<"*Wl4 e-U T8btq.1`CPsGGG{̙3[nMaJ@600hZzZj~~~W\o p…~ى;oڴI>~˖-&&& X. zvgffR'?hhhH5y7[^^c,443$̢Nb/-|ۺuׇm̘1s挝]ڵm&YRf cСcɓ'Ν߿i ͛7ǏӼ'''5133˰zCBgΜSN5<5zU5)Aɓ' W:N"<<<44TkkbwBe$`hBJ֭FN GGrotU~>>>;wHv>Vs޽{\֭[ϝ;^|ꕹyn .(NxeÆ +VH%QQQqqqL8q߾}PǏתUKL:#jڣ=z'OtvvMHH?)UV婅OyEŖn׮]yUqu^^ޫWZj%)733֭۽{nܸqƍ6[|<666FFFWP qسgϲtttKLL |}͛7o޵kWET9zĉcƍƈZ*WȨI&~dff>{LUN ==F—[nF{1?Ef͌,Xs5?ghhhkk{؄ ^|Yvm5cbbNKJJ*ɱ]<2-L͉'mڴiѢŋ/\gXYY)%(֩S'>ѣ IDATzRU+f?dWffɓ'vԩc``K*\~oP*pZlj߿?)wҥ!C%F^~ɓgϞXXX4k֬sΊxUf믿+UԼysgC}\:^7n8ӧy &\Bߋ+hAOM ų/_۷<''G(88b ^ĉ{'O޺uP_~T~=2ߟP411QR[&2ƍK򂌱CQ5Z`1cRt2==]Rrcw]ÏoI׬YJrss)?P9%200ʒ\"HaqOļI^.~Y > FZGI&99YV5jؿQpHHx&M!T*U:uw$G9Sg[PP@ uyF8W^9kџ򌥦])))4af͚>>>uݻGw.]P95/^(jR :t+ :x1'''Ϛ5}Poرc5c^^^\@J۶mŅ[l4v%[VVVTAccc%.Tضm[(͞;w _~%b֭ٳgÇ{ĮFNHd|[T(IK~;MH1ˌ ^pARY6<گ_?Śf}nHI+s - Qj|.]]׭[[AbbbhUիrzy9 h>ufaRj#yf\\ܥK7nD_kj^SƘs|||Ϙ}E:yeF>;rHff _r;(ӧOt;v kժuڵA"##) xq&ѣG._A3uyX>8% [h{TiڵknU̶̔d5yyy>EǏװ޾}[N;J QX&&&T~^W_B%_%"|[n<==7m$VӰaÄz`QC))7nȐd?ԩһVB l߾WRE=UEi1##C$y(AٳgAx PcIIITE,Y$66O.?|ڮիW|)jFRIIImޞcSSS>LC7l@jB^+Wv޽cG2jK={kZŧ|}52l/$T T8""_&աFATM3f aQPU$k)ԊdJj?M###yPKݍ14(Cm5QTMJJR2ROO͛J"B 0:w\B]t zLx>AQV<${w 0EI> ,4ǰN:KjL♀S›hԨQ>͍?iӦLi唔bŊqqqԋ#͓oT\Y&wQԩSJ+@2x`*P!?6-5F :ԪUKin@^]v)BdR̩޽K*4ix˛3gʕ7o &_16sLO>s/yZo/4R{/iii))z*?u/k*fC>|G$$Yytff)SUTYbxD/Fpp0$5>P&jHBߟڵk+fӢ$cgϞB>jժFF7o\ȑ#| j%Jq۷={x*>>>WT\FFOj/*TO&={Ν;>R%++ٳgAAA$޿[n=}o S:P=}vppx{ 'bܿ!U𰷷oժU:u:v8vX_Vrppl<عs:u,\pҤI'Něȅ LZ_xqʔ)&Mze~~ާOsss333>PrRGGr1ƪW>m4#GeiΝ;aʔ)gΜ:::k̬{?#ϼAƏ_+ ’%KC(YԩS5|ENb͙3IcV޷oܸj㏪nkA/+AQne777*TjSfM5ai˗qO@$ܚ($$֭[!!!%m-111>{nDD; I>wA ?C'.>>^a%򂂂5Tj Ԩ۷;88L0' 2lڵ]v511177ׯݻjTN{Ŝ9s:vؠASSSkk J,X79|||&Ow^^i&I&.ZƥK*dŊ"!L+KvO~>Aٽ >m p%ih\҇$e%>1d2m2Ɔ "Ƀ;/^Obe&~WC)]̌)JW6mZ^&#---?h"qwE P*PF .bC FFF*mj={HFIP JLI C4j˖-T4HcW.XvMU-F(jwѽ{Njm۶+d~PvE[Q5k(=/h'jI W]aڵ= :ag\rPcy]~/2ӨQ0>N[|ynn.c&B1{쨨(ܹsimb|_{{MFGG޽[.{xxL27;$++gUTҲnݺƍ O(RHJJzcN:xٲeӦMc}wp޽4'piƘJ*ZŁ ޻wV1cbb >e-ZT[֖\Æ }QZ1F\g-++ɓ<ZbR%>1j$NwQ8O?C1t=DhOOqRnqr5RxݪT+ACxW@e˖9::N2E<nڴ~ر#oV/_G5 *tT*єn\W)Z8k{5x}jSCCCs"g"H`TZUô' A tF )j %/^r{2mjTN3J RB 4mfHNJ &P" cQGuԩ޽{ӊ^˗/qJŋGGG6Ljժ"%&PhiKTTbik.\!((H>xwB_5^pAk|ׯKhِ]J*S !Nv|͛yINN/ ]dI#G]JW͢%{]+wlR}xFLF+*=ZMXF\/@lU|2uWYf9;;u$jF )iw}'Li7ԨF (ڷǏ%;vP'r4;+;{ȈDdSddv]ժU'1ctuu>dɒ%﷯lu1> SSӘ7vؑnIfff'O a#FxUNpY: "ٵaÆDLFCÈd޼y|c޽򨨨gϞ1G_~t$4ӧ_r/jhhcccc^///1uTR.`DRRm͜9sk֬JBBBw됕ձcׯ_3ƺvJ4prxxxH@@/bhh+ƘΆ ;hРfmm]J2jԨA?B$Ο?O ru5k֌o}!m322ڢ0eRظqǎdT3??nذa^BBBBv1cƈۍ,clرΛ7oE322zMm4VPP߽{wС|׆ \TTΠj֬PcRVVVzz:cnݺԱ\*UTm߾aÆ=zsNbb۷{:7f̘x:(uttyLjժ~~~4J oxzzI1BBB> Hԭ[^ԩus||OZt҅GڴiC B%&& I_bҥ␟˗&OMM|#؜?^U"j733Ϗ >|8O^:uƛ7op 1֢E Ž[晩?\4G1P7hY Ə1A=J>]?zơCuر\r5k 5 f]gϞ5j=\21֪U+(ѨQǏOFCCC@qRѦMݛɓ,j([ۨ9y͍ ԞPr1j)^ K4EϏrj2LMM&^*WHhU?ڡCz㌋-|qFo[PPP|||NN΁Tc"|bk^bȑΝ+_|ϟ? i*KP2}Jgn^414 1:tӤFEE1Ʋ\4hbJ~W3n۶Ж}[2OtO (WxDD> l88EaQ9s(Op(J " UV?xxÆ riգU]"iذ!ϯ)ÇSÛ6m*?%Jf[VDҟ14iʕ4K)؊CSLŕJNN4cժU^xSNSNӟ;w.pqqa|3gOVb]zvMEa͛TDS:ĝP!e ~x芛?>6>Bi}!NZOTTX1""GA1&& Jyk  _sM<1w,Y$|\ѡCNEjbٲexXhtQ nX/^hDӘhU"VÇTVH7o?3ƪW'Aa\Z.88Xi}BP,Oǎdׁttt*W|r fff֭[7؍7nܸPF z7o-[Hjժ[xÇgeff_\:uJiiiJ 噛(+$2,--yЭ|/z֔Լc>4((hƍ?̙3Zbt%33Si5ZФ}kccӮ];//[l7on׮ZLT#2:&] J,\^^hرcc111nyڴi?]hRvvvFFF:::{x֠J*Q/2_ eddb8JWBɄea@?|EQ^Ҹq&M;vҤIy"""&NX|yKKK^^^k֬UoܸqFlll(XϞ=˗/_PP~aЃniӦ7 6(\.߱cG۶mҚ 5nxΎؽ{M0o8qBi& lnSSOXXdѣ5jc޽knnߣGj=~o4n(ھ}YBB˟[TQQQLjϘ1|100^zӦMץK|ZH߾}yޘ]v4nɒ%餪i'OOfڵͫTQVZ~щ'  5|LJ܄ ТE q //B?_|ř3gc3f4ޞʧMƃNNNAAA5|\ >~qz'NP?C\r<:Wt".t]چKGOTSok豚uYp!,Fc.\PiȌ3Լ:޽Í9RMM3FI+=N:uŧL2ƾA%D̛t{en^nn.ek۶h2Hݺu x=zt5FXs^5JΝ (k֬).wxyyB\nbb 4i\.˛5kV+ IR?µk^zvZjhB|-[ 6Ф7R9a5jWزeKWPӧOWs+>uT ѿ5(ݾ}{q9uttTgϞkMT IDATi uw@}:uTҥK>4AhjNZ]FFF,i<ųkXǎ>-|e*ܺu+/lڴU:y3ZhqЀR==D۵kwկ_ܹsQQQw4h=~߾S7^e{F5_V^MZ[[w۷AAA|ĸ\.wtt|ٶm4?͂ Ņ\WW\\r={ r:w,)ˋ|%Ŀ$LJEIIIgϞMOOj\p!##C>&+5mڔ nݚP4R+hW ._ܽ{wL {Ν~8_ٳԴ;}#5 d:L>}411EhӦ֭[#FDEEըQCWWiii3W~b_ġCnܸqŌ+WULMM%?/_4["?gffGVRVqf9GM^ > ?޶mdlgΜ@Njժ}FF_fffRW>^fddԽ{wބ]pp0Oa?W^U?MR֭{왅Ň(۷oҤIPD[`iJi1OOļyF1aܹs;=k,'| +5biӆ_%cc۷o/^B ^^^ҲeX"CvA1V}[G5( jcժU||[ը![[[MHH>}ZrZ?~QcNJ}ҢӲ-ZFرcThutt[VM←Pc$Ss"##$uʕ+7i$U73g1ljDeK? &TQݡ 5%5 Z 56nܸB{iK׮]iDr4y???MNQ޾t|(cdd$n5~>}+ 5zyy? Nt !!!4ڨM6JxxxJ_2 _~}RRRbb"O!~ƍ#G={L[AF{G~$ÿӦMO211ahh۷?~*|j%HI㣝sssӧϮ]wяq˩i޴{A+>PٳgCTd7n(O!BӮ8<קZ 5"x Lݺuq(9jh/I1PcY9111kVǏǷXBlPc APwamm-S:HSQEAAصkd 8:(h!C_ 3(uŋŻrrr$ϖ{xxP5U,>x9rnbK.U~A1 /H/>jr`8q+6nܸ1{+j 4JoLlnvvv|yXXt5Gcݾ}=5333yoVD<<<[n3h(@Y0{쨨(ܹs J6loOfٲePjh}khh8|p>{COO>xSNjƺ78svvvrr 3~23ۗV@-ZTnrL֩S]vg_^=+>>~Ȑ!4{FUc۷o8qbXXĄv9;;h͛c>g@mD%U;d2cĉ<|'҄eM.OЬY3+++ Ο?FmSEm۶577 ;v؁$ӧo޼Y[WwӟG9rYPegg_~=33s޽ԘQd0&""%4c8sxY*M|W @q&MGDDhޙTrxzz7jLi}kaaA9czZd?.vQbhhȷ֭~~Usӌ-Zܽ{W@iݺ5_,+'''##jժ1ZEjgdmذˁiii^Ù(J#92d۷o5k(kjKIkeggi. F`)))|r&jyAΝ;7}tùo߾>o||d0E7n˃$O[&߹sWb 5_lف9Bգ&89sP;ԄA>}:E?,9*s)tVHF2h׷7nQ2M4ѣG~L*@){o޼ٴi޽{*oz깻 ۷ֆPʳw}S?>9JNٽmڴi֭ƍfk)ό[jw^RRR||?^z2ƞ={FFUG8S/ϟ?>/%; tttܡCMi ! Et-!!!!$$$++4hЀ~b &LP-Zxyyɓ<ٳGUre9ߑ#G5hРGw܉~ljՆ ~8^{nAC2?uSY͚5+Rp… .32(e_700@wnZlvYRBsss?R`P+Kutt;xbX?R>??7*VhjjJd2Yff\.d4I(###GdEPĀ]Ԩ(؍Q?5jbh؍Qckw^P])[fcp+*wf-޹W}m ԮӬA D@zDv3#w6lpΝyE~F.-[fdX@GG`i\Xz= * 0 ԨQc+WLOOm=}VlHyN:}A!}<(MSl٫WkN(>>>_bT8~K*%ߓ?;)@D$όk׮GY3%%eذaRʹs>:t4cJJ깛6mb~9W 2-Zl͎;*T@D6;//:uю;o.UqRioj])Y500K.DPM7cƌHH'NHA|ʁgϞI)SF;Q_XX޺Q#ȓoڵk+M/%Y-[H͛7WW_\Ah|ÇKH2fffJxyyɷX|}+Ochx^~xҥ˗駟bV*BBB{}###岵Tп3]*ܹs'""BOJJS|3v jlW\qssnnnu)UqcƌŋGDDIEEE1PX?\hQ"JJJ2d[=w֭ϟ?_*q={ʿ7O~Ϲ%]tOmڴ 0MVVXѠAɓ'xSZhQP!"zjJO>eʔ5jtI,Yd޼ys˗//U.[sݺu[~= Yrc~wzruuqƬY>3"S}/֭[?0*Wst9=zXނ]h˗>}jccStK7Ϙ1̙3 m=SqKvR y1o ޏo[$^/0)>%amo"gwm;:k@ Q#DP5@@Q#DP5@@@&0q\ڡQDSDO1ea)8V8"dYAYoN0EO_t{ĕ'|Q*"1ZE< ֕q>.s4o ݈Lh5#Rƨ?0DiZ;'xc+g;+[+?·x>5 h]I &~ZRLpp *CkonD&(G>4\y8"swq,6W+\d}Z>S^olF '-i1}C;\y;xXǕkg TnhЙ\eiy+%v\i5)A`JLFsЫLFs `J5+W!j\iLFs%FӁ|1h0z5 A`ЫLFsŠW#DP5@@`DQ#X`B5@@`p4DP5@@Q#`R5@@`8sP A`AR)`:5%Ro:5… ;;;)0VJMg`^ YRky+%v^|0LV!j0Kj R!j\>}4 >2t0Kha?~ƕ>} %K\zq"b{}755J,y͛7_p!22 . =CV+ "rJ͚5A +:;;_vիWK,rʩSZCFV9R*߿Ν;OlР] IDAT5k3ݻ7**ɓ/^(**'"??"Ybbb%h p~֭SN 0N>}5%RN5󖔔t"ٳg˖-JRwޜ+4o޼K.ڵ+{7JmӦMG+WJ%IebYspYZJiG2|Z'1{B]OYeФic%"@YYhO2,eh%b!-W138B D tD$ٰeQ2 Rj4Q12u(|"F͝;СC.z{(沨J*ZiS)9B}g>Б쾄S0¦T{&FΒY]fͽӒӓ>3%^fx)DL^otǓi4iA}g>alZ)o]vy-s-z2 `˜J}Ѵ'">TH*tiyٲe]=T>". JH5W\ HJJuS_4 ۛD6ݛW ѣGFFFO/ԣ٧D~?N  Q#XbF2˲OjDEga;1nժUCX5f6*U=<,JII(B&M҈hɃ &:u?ڷo/Ν۽{EJ{wvvNNNkQ#]bE`DBFK0d"z葯5ko>hРqVNЧO;w8qb޼yO<>}ɓϟ?bxxO?tƍKOoѢ?>sRJ) g}`r-@Zf٨ :tsDTV899K6L_ŋh+ dG.*UDDDW+Vl̘1'N$"ӧϞ=[5e٣ht"tiΝw۶mV\ثW͛7?]ѣ~T_/XShvi>EP0- [x1c3fΜ)/r,^ܹ7|ioݺ0AVVV[jՑ#GRSS֭;lذtPf͚ոq;vLAVVV'ND[`IJ?_&KӫWǏ/ ]\\H@KVJ|iA`Cbb#Gw0j-T:0- 5k֬Yfh h05 h05F0A j0h0= j F# j Fà j`Z0A j(X5&G&HI4aTJJɩJɱL׻>LïaD eh05]}g^ 鍦0}go=AeE`D"Z)9el*%kTJJIdvBHn䃜(HLn?v_zbfqv/ӲOڕk6gN'Wk7AF6 1ɵ#/9+#g0u+yry"ՠ^ :Zy^ Ҿʖ\ZIiX*rv֝s֤rFП5Yc>WqLh432CAhe<j$`Ѭ|޴oW f Q#ӭ:B84V-`Fjx: &­ƙcVY`5|vlqLjh.U3~EjAsgI\>TtiQ#%OF"M}0;ݛz|?L&ƙo,]@` ر]̗c`_c@ ŝVOgITtiQ#0ޱ]Z8k)/`0ܓ7f F ac#4;keeZڣ Z a$qFTti Q#ȫc#4Xm>ꄜǎTFyqRѥ>&D#׎` ;jY۠Ri@6*YLk.!j(9;6K#ڡm )-O98KJrRѥ>2DŠc#4XRV8CCS0’8,]C`i;6K#ҡPws }8K TtiQ#;6K#^˅xA 8L,]?Iѥ" PERj@kˇ}r8)KEtiM`yV,uh Ӵgl&x7^}xA$nD5||(hx ;Q=1Чe[#IMt²L-pDדX*Q%o"Nvػǚ>Q\/ YzǦ,Ua(15D?%}?'6yz5@Ŧ-=faWG;{s/١5ZcwQ䓔3>}ݻNNN]v5#9<Q$t<t ZNE\@3Zjڵ&5Y&((F| ` X%"aL0X0DCPP(qqqh T`F03(.]ĉnnn䈈uݻw/55ǧʕWyg&00yYYY%K;vlٲeW~Q…ƎkVtO P|&MtQ .8::~-V؊+N>WxZj1/4D`N+V#&$$d޼yժU˹رc.\hP?3Ftg&eˎ?>>>^^sW^ G(@̆|/Ǐ ]~]<˽{ ֌ ʕ{ҥKD4h r ic8@fCA%$$D0y+VldR`nw TpttԯPsi^5@~qѠA{;"Zh<5^gE8BBB֭['oa޼y$~}{ ?>Y+pdX!"܈h:VZ… uEGEo=EziLj|7٦~B a@JNx6?o>(Rr)Kv3_dZ]Nr"Q$M U~˴{nt"+m% 0`VNć$dgbu:΅(:;!F*,vETJeSDWʹe=',t~_}egDl Tq *++,")}.˹`88e,Bq VD^ AR)՛v웤ﰑ "cپ_5jAmqH" ft8rYDIQFeY&A7meY[S'!1 )X,1 #!j|Rr*3gee:݃.^~2ޡn& k.ҦS,\N^m59K5>Z0R s#FvBD#;Wu4DTN 6)S&E&8|H`#%7\#:p0-CQtsѩjg?7?9bfS.^"YdYƫ_2=4h_?TŰ̍ky^p/S3~BDϟ%f DZmP(X"bYvߓ7ftTWq 7J?/oߍ}t~u=7>+0LZ 98\Nj 'C{{o٩g̲?ݿsxnO;&zOwJe5{z^ ֵKGv^tGS]݊\v/,Z[`I51bĈ#"##333ɦ;v8{l5)sNAO{{'^\iKFTxy/y^ܳ5_bm+n[WwĘw#3#]z\{VCZ.gH tptƛS0 h^)g,Y?Y[+vJ 3f*":z}*GQh@XxJruKOpoz};DYLK,ð 2m[.KKM?N>s~un0ۙ)P*ْ(8Ů-kѫ% IDATD/_8u4eSgffLG ؎:N"el]rDɯYәW7|8>Q,B:~[*/_=\R zћ;#k3:*Q)vzTT5Ą4Zge]rrzO'f*Uԧkaw軪R)nzuER+{B?sWZE)+KJ-ZNR_w牓m@+=y^o Odkgߩ֝{Mp"9>p+6vo增xp_#!FF< "I qF6uԑd"jдuɲCm8:爰,.iJZj ]^Vy\aN=,339P?DAݝlFt!#ea&>.E**]$ux^k_X3+kNog[ӶkT/7wY֣TؑD~I.e;wWc}Gm +"DZ77gWL៾ǵvJg؟'J*\Ο0vjq;"ڳu]t:ÏˤC7s/Z,͊[A֝]:ӤERejX4CŽP7., *~&7dܯ] WqA8voF28F/Kղ~ŲAB*0cb3D"cHRnYArXI{R9\7F Fxs| Mu(&:Ȝ+us…sԘ,猟y{)]uq)_v״{B|7lV*kSyW=*Uakgod;WΝ$"ŊTZ֮vD|t.??W*x~VAF,]8j^O FOBF 'Bzٓ͘Ԕ c1֐Sҭ*bThB/ D*e߷X}7_WLʹ ٝk2>}IY!N/DDG^ V6}~w;,WV<,VSJظvs]|mҧ<7S.%qҟ/)S΃b|e자٤R)&$6m):qI!iiDԠq;1K_]yѲN'_X5+K{>΂Č;\y쿘췔( VqyމӚҼ=&aR¨VgEݿVϵ^!]pebs'KMѨ0mjϷGٻue)վ۲(:zk@6!#eE7Sor oVoӷ;r#+KT*rGJۥUznQZ?w{J';EfĹݺ~ V˭!B@+a֝kojg@D{>r^5 >*,(~ r͵ yn uM=cKtTԉOvΓǏt٢]8|t+f^?nZTx44s?Ι_Q$hG#3 &({f9X۵Tk_mmfOo%)AvLk"2e=(#Cj|;/ӯ^mqa5R*' KQO3{ir-TC&|DѥT~f7mim]Ի:=-#DԤEBed'&8AyMRyu0 $]݊#5X'g=:9ϗ 9/tɂ}u}'tm;G^K}XQOY~w<5]~9wf7ѕKnMoC׷*kG{6K6]b̜Bg:ܶk_AͶ:jܺ_P3ʔJ;of4Z-_jK)Go;}ı[ϞfI9MJ_jGDϞd?nпg]aɓ;^<{Yb.<*k\8Qg;vj|p\}8B|G<9j"3IλH#sLyǽHaXFudY&>{nրq/[;EQNrv@~0 R*;Q|u!u#eckؾ{Ѿ?%R:{ 9U^'Ҳgcn_DDεY~u]>*-܉55ޣ }JDi݋فC`AT\zmػ#uъ6=FDiߎYv^3eJ%%%ݠ1?òLΝN#v}lܥ{]۴9Gx^0YDgi3`YfÚcDql=U\S܏'9xFaB֟zĪQiOFTV?j4:-@8{݃R^&;'GN5*UT Qw;vگ 2*H"jӹVeؽO,7C0xTػm}<( nEFEQ;ޥ˽فYC`AԨue[tPF "}{.q W *UP_mx(4a|32'H9rΚ!JJzU9; \qޒH97oY&%%c39)DԭG=uBw Y-=pviw3vRkV*wt8Sp:~f~&3r(c \:0~RHP**NFp\Nmձ߾#{7tFiQ.I)(DQ۝}ԔkuϝHJwtr!"Axd{I6 Cg( Nvgf /*`YF22y&3&LzEDq)6 KK ;tQ'8vÚS׷ui]Bm;1>9:fK懦d>qڕhlB+u^ 1`h ^{L*`aZ-ODZ-JϳGuMfX{)qxg,1H*iFe}BE@h5ZBٿSAC"*~u\ }ؑEQ8DoQDY5ll<9?bru0 2›8V _KJJ|u|E^R!R0ᎸJ~ Սl-;feEQQvTVV[?~pW?(MҲcO#9B'8jRC60^-h_F` JVϟ͌߂RTΛ9adĒ^xfҏKAx۳^@DaLּz1 gEvBT_)i\)JbM96fV9gwL͂_Lx^nTUN]k>ϼ תSx"rP3#2t|㦟K兿9{/]ז39fx{~㛚DIw77"JI8rҤoK Ź/{um[mM-q)<%*vdm".пe}J.ODRϝ8[K;t |؁?T*s=L;w0,;d7j >G\?s+]`ƶ>.܈8,`񼽂 >zРƤKNb d796 SFi興/5[!"gS<0T)sӆDtgU0/Rgwn?)SCEe:Ib8+˞>I"" b.i}:-ݗ:*QM/∯l>IDVUlϾt:(DZQ8zx1vs뛞#ckg]s/.E1|5j7Pg煸ؔUmնZ2YUs(Rb` (^;^F 1@EE;1pyc8gvwvvfgyRK>%dCHh[ΆWyCFz)ǥ!w䜬 #phh6ro]N:0.*.niY6uWihLl'wG W X*cY™0jjJͶ1ʹwUW,î2$4}ZjXLq9b9,˭[y.㫗N+0)1+)1!$E$w&Daէ#`PuF`Yv B$E͉Y]50yh9/JEuuȘ{v|cjjȲm9nxX@@jq,>+IZtZ#3RTmE"JvbX(;},ModŲ\g7CB bTf,1tcO^<.(#c-;{CUXv\UMҹ2CMcSmfvՆ~n/LȠ%O'@ʠUG?-h̦SQQ4AO?f+[us"T2lƁ(emLp: ӵla5eF`3d%pgdbnfae`lFӒa4ue??sinNq{3OeUa4 ?bpиIuF`Y F&IA@Ht xYÜT" Lu YZ4Ykxۺ)\H=nfZ*0Cl$$h(Det]q$4޲/$ pWA9c9?$I$YmaH8߼}P˲[(W@*1pÌRmGO;xMFH$ 8LCr/j`0 `,Wsp0?W$ej.rSߡ  aY+/ 0 `0 `0K `0 `0 F `0 `0 R#`0 `0 ``0 `0 Ԉ`0 `0 `~Xj`0 `0 `0?,5b0 `0 `0W`0 HG ~iss_>'e=:E@l‡̌uu%;`0&Xj`0 `0?A~:5Jb_һ.$q0 >vwk+j"=-/9)[C"54T4T 5i+ 2^JO-XN[Oļ!CӸ01 `0A7@N?(ADpp,WVm㕚UUUlھ]=h CȲPq?AԽۚc\{ݼ/0VpqEQat\ `0 'B(y  DEO&Vџv|H֤O];6_52rvG)6Co:/~7"Xs搤Y`)ú_{M:#{b{6.zͿP8w@KWCqS|i 0 `0ODjjDŽ hH{\!`⧩-E/}w79dD#(+GԽq IDATLxxMȓzcnwvKN]p,;_nj4i+ro֯'5 MX|ƥӇ yJ 6q6*KKӒeL]R#`~E iZBA,Dz M?&I 8ek|StZ²,DZRU'OUX?}W@@qr;#Ie9a6.>($8ae_e9eQʗ#*z% I$`Yf8$("HIEikW I$ CI莾ݾ\~Gυu)I$rCWzy#_ Ts[$H$H8=ZM-H$Hf_Z$?Fm$ X7WhSWOTN F$RGSKGĥiN LzӇkexΒc_۲$Ia (`1rE jjZNM5SUdjEQFup\^ZzuS$%}yx|FzOAüouTKϣ%d*++i6ma3rl|x/~Lwkׯnܚ+wv )9)I9AXwp4:LT{_P(pmbUw~B!uf!#>eg ka76՝{^1Lld4yHصF!1( 49/R?(40Ԝ.*i$IvsE'=&%9'-5W(Է2hb9tT{--AZ&pȣW_&(*;?ŏ!5u~)X/}Gg%=-w!OSSr7pq?hW#g˅0V XӮ׉0yk%bZbcR9qM^ԹӡѿgUPfYv1f#}mJJnjJ}Cf-lGcIO|ElK(˲SN{>*%SY4ɃB2SJulъ'C [W̦iļ~cS;MDvfF}=ZUIQ7/>#1!.?/GUM̽g^*{! ݾ",aӗ$'~8k}bB֮hL?|B\tfZJa~E}V:5ɏ۟IڿNء{,0g-SPPR[Lgаq>td#>~p2DZO8n֕ 1}P(/ο=Hiu'ZS&~)z-lʹpU`036A1o-c|-ܛ{]CT1I}ѝ+N>g.Vn!v:w?qJQ(uwm; 7ļRɣ^ 9|#~F5>vŵ']'l-/WTZ_ fr,IT6 1Ƈby攃psǼO,=}~ dY# #mex>YXyU,c4D' .4X*9{@,{ ~_D"s[{] }6 ҵɟG'B^^nQV >gWyGV% 5;ujW^k*A9&L[/S!޹9F27/k(? [ad-W=x[-hn4[GmA}6@~Ovq~~I<|bJ?W~cf˪fVSW:wuSc PTjW3}Ns=;nΛyn 'zٙ.9t)[^n8>{uv{R3&8nGrׁH޺%g9xY=Y^ƭGKo0#mGs4khSA7,|084rwؕ$. 7V͝PFm˫B(гaIQOT>C3M‚z'}Pj2l/|/XQQ'IFGGߺ{ld萰;f؍pu -T|T{K߷u,ٸݢEҾO<'8?K1 K@V͛bhbܤ.2YO/*g!vkfqS N]?6>]S>M錾'YIlh\Ǖd& ~ps״#k38}#SM0U{-x5[f ͊J~YkI5hSA-&'e\ꥧ>͙vP:P(]̞5Ĭ-}I$̡Rs`攃)..pu[or'$6!:φ5ru~쩧>:_A, \jK(~tz "JMM ∗ (=c#+= ]VFnysHgTSWӿ1˰oNRV*r1|Ss8N(ބtFsK}Z= ri6_}./NƫWuF]|NEeOCbϝ훮iQn;M7M=~$H" K~a[8pOУO cmaLt nW ѱ_&FFZYYA>FSom öo6F26NN9z᧏{ozn#I4N5#=衇(s'we[j|G>uƀ@v OR׏ o`=kjqAMWHDݚXD>O8r>i Cr4oe7.W.'|H۩k(Y=w&V]7QKLܿvvVafF[n-e1 E "t>CjɫL9̫!+fKEs&-yjhjž}}aƒ?i]BH[6s4˲m}$޵w^;:u뫫/Ur90/;35{ oxڹOb &zZjŨ($k"hܻm96b]:u㸨C7 38S FE> ~m| I3S@EUC> ls_>}bL{rRk+!CFxwlX,߿KzլmI(*,ERM3И|hrH8uFܙ1 ~c}S˼r1=]OӭԴkwڄYOCRc#K{C"O:һ٧:U~-n}6lYyX_]=YI(IKPwmЉ): es0/ 7]:DL7ijݬm\l*'M내ZTc}a޾N_-.ung>.?$EBFx05ah:C,J7vo}'kLAAc>sڴs=ە=w=Kk{'($b']aD"617l¬=#s`G݇tFcӍih Fw=`Ǻ};kzۏ\URV zp⋰>r3' ().OK6V1,ð H$"Ŏ]E(ώ ?T{8:?3b40mqxe?>% "_O6+t 005`GEEaɪHg%%QNJ okCv&V Q xuYk\oosah +f ;F1aBj_vmJwhy]ux.==H@ꪚ6߉8S۵̶?F0LM:]up4#*Mqa{OW7 H^}3Syx]Գ~C4ئu[Tgêש)j:}uSirHj~YZ[K$̐ދW;p|QojAV^&L(&uBi?I Иc9k[ SvC%2IR/o.ݚhmߝo0,u 1U;n$4{ma^g$Bo>𗥠@+)@[ON,Anv&XԷuF4kj?{Vް;Q"wӒD ND\asW#_ࣵS> Vl?²,CRE L-ngȵcqV m6Ff%sWihj×W2h&T_uL(|0?!xW#`?X[c[9g<ܺ|zBD };C.m\:Ӳ6szA%D@p?).*\S[wԔ^~SӇwkܩ2x aD :_ ?pTaiin_]jҜz'y9Y:7^\h0jݹ乫5l)ϝ8ȿ0?ZzubE!b]tE+l>}0P+;E߬$%4 )/o'I@% mΛ8ս~]P)j˨N, +ɞTRV18S}n@$"墅SiNYUG Iҿ2Rm~nO,gh[XXzYK|o W_ܽʷDBwOa \s)/@{Ƽ>[PKGrJ$LN.qzHT~n{wP%yث[=1 ;fBNJ2|[MCT×@ZCD-:/I(,5|j}UxC2:VV&Њ[U6X={ie! Mp4ai!,e(UA|[ݡ\#f}<ϖ,**^f)(fwLKYljN٬ϼ5Noe[zƒ`1t%|Hh9qP0,598DŽ $dZd%ԕQ^= f/wU/^K͛ZKOne$@+ -mSWVX(+*_D nf};o6mQYYA0&ZͬƊٛvsҤ5A@ݕQ(ѾCn*DZZyƖ(!rF.t*PoҴ-msڷZʧOGgg 7LlЖqᡱ 4Qm E޼TT(ao>H* f'޽}S(4Ͳ )wTcYxYPTTF ð x RD,.*ݝ$}[V +!q@dgih̴.WR>U]h10"$ >})O*yqrRUH!--)~"4}TA~aaɉQCءtZr"Lo-2x?}p`0 E "ŸtݹI{]Gl!;vò,p  6tr,xAbqy^NvX]VR!Q NC@8 /:kܳWo!3j-Zǡ;r@˰kL }t'&*ʶ6f2,Aqp1HX4ZOzAQaB/>rhIVXz1ivqQœ\^>ikoz"ege{ 7!6s4B#>i |߄ bFV'αFz+,,%SriiB[WJj,jK%/,,!*۵jϫQwrEpڜ1;{f=:[sΫwILDG?cڷmMA wdhUNE +W@?_7i+yxΊ]ԩK=T2$ɋ6>f IDAT¦O:]rY~YďaRPL[ K'wҳyrcN񈇩 q &*E(}a̺&JKe͟ \>fZΏe34ak߸pT :UԖ*..54OXj`0Ӽy~m۶̙3OccW^՞G>:}4~4u%ʮFMn3ZVi^zKX".;uph ' ހO_wgab{7/v,\e2 {;'~|:k#ZFqo#ꎣIhޑ=?KMN r߭'2oѮM[O%bqP$=x\A~3=}z <x.ˏKVK6˾κ44vۧ.@@EU^7"|p T']]5sp|[B nl$g^saiyquVX-F/m:w*H'r쩐BF]a C2g#mGi]5vFZ"H;u|QST !{KTs~GKu6oikh, MY^_0(֞zJBH.ܩ< "E%.}O:`se )ھf~bBl'9Jʪj5VZ"$5d]P hjղ㰪 eUUq>7wsRPBE%Dեx5 d*?&*`0] ;;Ԩoę1Z7P\@Šd' 5J,i؄ٲV8k IEyhR،d01ա\Gؾ%nӽĿbQֳDee&AwE~'Et%c/5ݹҋlɲY5IOX\'lnIпV6 ~ qZ(]TX@Ͷsk@W1hj@NNr-+Hڨy"eeJ*]WsE"J\w~ f!aY޼a6FǨ7"_~D'sFf;И ;7#Ξ AƪW./_tj񊾵E$| m'wgStțT+*{]ʬ>UFS,KKڣi ~3'33`[VFCG)R>a?JOhE?e9N:څQj8R.!uk\PK:")GHgS~Q â^QjiWo4j\f9} 1 M5k&ܿq9Y(6$T9*M7rrAꈚ'ivUTa. ztF]y$I~Ԩ*0ID2S2BR#|iӦsڵ?wnvΎRDf _b5tDM]@"WӷO]!:(G&cja2tx8D acwճG srae.)IR|PgVU]:6EQIѕvNrWz६ _AQ.Us~W^/lh=v#\]쀘Lt>@JRN`B!ER$r1ml>LE$\O@̌=RQPZG31Z}KgV6"J]K\N[9wNQENKt)ή][' ]n@yYBo=ɥ.Æ%\\ib)-3goO:8Ƥrf-l[neg׭8G=9Z׉H45]3}U,˚~KtEAqsbt (}]V`f+8$];5Jt9WÁ1,xuGR3G g`IBI9|fTx|J6ھ23IRI>TR7i$Iȗq,`s``Θ1cKtԩSN%Jmg#5/JKZK,:Ъ_GQYנ;W^>;Df=3;3*Kbf-,9xuzExxۗB{sOZ_]&H";K~~J*&3dbRȲ% /X\TPG1`T5u]e3,O f ZxIaoǭ:ȪNiζS(l:jJEyϞ95=PH=y6 t?T"V XZ}} ܢga|̌|T2B!Q2Ivid-/@B|e=}P!==5׽lroؒ‚Қ,)DAl{$./?O{}tmIX0AuO6-5OG(B)N8N,-좸T$bI[fzt_\Hph=酾V,úx >Ek;"u'z }1 ?̛q;~% 9#H"=CzGuzD~~I/&qld. c=c j]#./[:cԚ]ky8ͮ g=|tr~rR Iq6'ceDfekjeHKVUPВ kwy7s634RRQ--.**mÝ y(J)(..}Svvׯ߼yS51q?~]999џ?:v^^ޛ7o"##SRRY,GGGGFFiii?ny{C7h濿(HӤ&%~VWPn$%J2S*:#H}!-% {pzAez-c/VUqۻ/z܉Bi**̪H%EDe9**8〤U!k];*kv?|b@ч&ʬPf$ &fU3Lze5TEE Jm޿B鳧Bx(ܳ0 ŒYշv zAذPTyw mY3aJ@S%zf;5J/{B 4Q9&d,_xb\nΟ S(z(*"_Q巘dG]%ǓyfxwcDKjc5I9?F^ZVmwߖ=H.ɲ4uhGoe3D;q]fvYVey$7PfྩeV췍IdBjͲQ+.* _|=s6z[y&׾G=6LEb1m㕎mݝ .Bq,gj*{Wi^T_2nnߢ2UqG_~+P"!>]fBj=| -Z^>wa~HW߼'1!NxO~ls"eI>+A|8Cdv IJ={w$_1 dҒbJ h]n(}m<Wϑk6'R#QZZ:sL%%%UUU[[[kkk$n޼)VVV5rrrֶr\'O6lؐ$IsssMMM55IHH۷/A:::A|S~}   {slmmMLL/_.gݺuA흝۴iE^5jAzzz_^AAȨ~?dK ,XXIk"C-2=}/Xߴl:H RPTTR5sfگǀ3^{o#[_=}eHֵ8PR5e~-EtxYY T6umv.ܚ,yQ#{ѣSd x1S~5uޟ`S< [uZ´r}ʋr <q(@@/ܞ(,,(=uIvKQQb[_-RTvnkjt&O@bߓw>N&IȈ:z-|z ݹm㕌|Ξ<B!մ M3CFJC{z=w:\AA@D>9)4x/ɶގLn^CP$(+߹u3h_ KزRnN(5%7J%.Il(=ߖw$If};JZ6=[ͺ}#\(dY.EB'泌T<YiY5wH>eh-kWkd2?~V,N=}COIΉI%߭ k)( W,>=sA(+vr{ yuiݥ \w8**[蝽:Wuup.&͚z(6&UQQ$';N.lTJ"dH[qG_NM͍zXV*浗ޡ(R "^$tY~FΖۻ^ P ~]\Oo#n7/}!oټ|DO#{%} CGtvXa~biIcO,x>;be_:ctO@@DDx𘾾ٙȍ4uDFy)8 ~psxwOKeOPPT(8x}i) 9?|2 }tg"BIqk禍?rApww-hii}ŋ;vzjΝQϘ1H8ӧ߿߻w5]v'iwvv_.**ZbETT ##aÆ`iiihh˗/߿?66 ͛Nҍ5RSS [`Appk҉>is玂Bƍw"e"7l؀n6mZZZ+KKUῈI5MӞ>]GS:v[*Gĝ*{~A Mm]z枾cʖ4-stwr(AaA+gPVZRwxx!UB]*UwXR5> Ah UͱkBx,ˮubdv9Yٙ3Fv6AOBi +S%4ZOW< 6BVOOzo gB/ >.?+^trOG]]ع=fs27E7X`S<$ˮcѥ{iq\حم ~n\}$f fy/f32[0x[Z:e).{ mVm8zy0wQ?%d콩 Mt/6}S\'`㚋\]9ǜRz7kh$04r{M3I~TV*۶jzGo۾Zo1\N0o녅IZϖj G+ZziѬd;ܛ44 mS1[]hDBŦ huDl#--9Z\^o^b]MHH IJJzbQQڵk-ϟhiicXׯ_?s ʉCX|Νm۶!111^|̙`hhodd[G=P"}ԭ˧jYA%I辭e%ӰwzvIH-˲qD,Eg$)8%J*o'UEGEbͰ,//"յ*5-wK$ʾ /((*}|yzZio댘gZ}yN.Wyz﫵qC'TѪث~jQLV $eko t ajs]dG9Sh߼;ˇ/PSS3c5z7vb&Dt_#93mȬP ?/ تԝdkgMѩEwxEk=[?zR֪'0/r?. ̯Y`b-no<1^tMfeeեKss~8q2!!A6ɓ'?| 666<|0̘1cܼy/cFi2ܺuKcϟ?wwwk׮!q̘1񇡡!>yd߾}`߾}Ç<|Ν;T]L,=ǁp>$I\vutnRuu7/]0;o枾(-vp$IFG0O^XeY8 ۏ\/7,ѹI Mٻc{z'Ζ+6%tjb V6;^i:91athծU;n_Yp Xnkgnv%ӚjYGRdY=֊cݞSk||>j+/‚NFN?b2? E}|]rĭ缫JGSc7)!pEڌFCE!2.k|kO !I$)VֆJJ>*z%YV*IO)RUU4110Ԭ͙HP -!wQo߽{z IDAT"!.AE,EPD JP ˕1qǕ!b3m˽ G`@@R!ˈ#u=9:b"zn3fU*))ҋ4>.U &8TH h1-Aw7)#=ˉ+R^Tw'1'PeqrEp)Uؘ"ko_77{/I )(P;:لԪ _ar ws--U^N~\4 R(M֚lOvrT*EZZ[ EIuzq*"1!#!>@blLq)L#\moo]MeJ8JhVkGSAuRiScS'ei:O/'ogJQ+QDAy/V*Rt:ѯt2Hede99IYN6NZMVdS9].7H'&d::ZZ Y[:YOZ IW8P(<ϧ$ bki\R*Ui)REή.A)U9S,Hyi^rVIxicgu;B~i5&Sr(;8{|-#DAHOsvu9LV>Nq^r\V'fgZY۸yz;w0 x@HwrRg PJ @Tw'WwV9WL0r 4JsDz.  ?#'OH!)JV|tװ/B^щbcc| 2ҥ{odnFR?:_O{FM/ܢ}N'ZS)(n^p~L ɴ:QTH՞8UܱS^/إ'q(P +B.+M .owU GAL}:pzc}0#)q$>ش?b/iݩg6]J?L֥o$y[gW7/NTnY὿ѭ]XXZG1OQaZNE m=VReQ{Reը8A[3 -]>^EՅO(ޕ;oX֠i`A8XsyJL~'MCwו* DAHO}pNG*Oq<3D>G@U\xTʫeޙ Rxs]5ڧjSq1Q§#AA^QS\\D4j~`i8(֩ M,>j^T|Fjwrur6۞N ׃Fj8Pdͽ (ZW;WҴO)LQ$Nss JqFVAlfɢO}^JZ-omm!ۨD'dVNDhR?(.&SrTSʉ=J!"/^+X}nF*PP{$'ƧeAC~ju?%%O9e§E>~ѧSh֤ak k`emSXuYq(p?|0qpWOJN&aKfO~u6sptIMN:v`';=17'kn-vHK9qlnv98:w  }ƟvOg3z6[ʼ79(x* عi:P-X{uc eȌqjȐ!DA~fR~fɍeBD|R+wҤIyyyԇp _;߫Ts~"񽳏=^z֭ cufX{o]rf5Ɵl7>\MDD;YY¡DDԠk?;!T)o\$#"++- vOrbKȭxL~^\0Ctv0|L.Reik}WΟ:y@rR|^N{&^kQHn}jHKU < m 6:dqeA.Uv}(Btt_Ds):ӻCT3١8^V#6%=rprUaAJ4;`LovDQ\lΟr"}qv s*O%oʕu\E?l\E 3C[[!C4Ç:4))iΜ9B`#:88r-ZhѢ%&&?~׮] +V;vTԩSNy~ժU&Laʔ).\0`PPPrr2@rr2+=O#Ԉ\>,6;XQ (|mh'q'cгa:+"zOm֪ۨvƜLƮ ?=l[;^odlj(7%B= ->_Q?*Bٱgoq6Rb0Dۯ;,> AGC"y zt賱4j0䍗0@ GO]C*4XD&Oپ} g&^{=j'zfgg߹sGΝ;YOZuYƎ{QTT  K.&[߿BBB)V k׮,:^rjͳ q' yg$:6DmJxʦAv4^WBgWʍlJ+4gh !兟_``L?f "0`eee͟?_ZjjBXr%}!!!999A` Ezz:qu]bNSRR䡎7Ν|֭,TڷolN:W_}?,(l>_d5@yP^L0?ܽ{w۶mG_QjX.q,X?cƌ۷o;88;wnܹ[+ ;v֬Y<M6aÆ ۋ-bՆ ҶmۣG?ҥK{vqqIOOߺuMh&I&Zbǖ:`BqС |Ѹq_~O>ڸqYS+]^_rG2󩩢X_i^^rBSD$h428DxR>}k׶lrرcǎaqϟfkkkV>::z׮]ǎygРAkז65yԺuJKo޼o۷/))8WW׶m۾[ڨQO>D[baa1sL\ޤIgϞ^^^R%K\xQ9;;w=<<|޽~~~y}\mg j #B 2rʕ}kb@|lj(sǿc4d[ݵҥˢ!C^hqGp-;`@`4+勍g>)^6WƿQ?CCK<|֫kNv/fjⅦWAB5_WN p~zuqjnnǙDME/-KE{߭ -?ݻ2kmaw7)klO߻gBD3fq$rǏhвek֘ }eˠ=o>\ַJqv׿J*N]8"zkN߻7׮zB② *;"O6^Ԝ9J%eu%"A\RG^z4+5U H" Rz̘eflۖ AV+ѱmZ._&[ - ۛѫ׍?n~T(\>_Y_&#ڶ5;4 NN޸BHs8U<|8<*59уh"  vvuGj|UF"8xmZĉM>YGR}s Nݹ 0gdx~q  SFIIJ ;鯒EE7^' _XY;8Q[&q܃75( C|n3YDjEEPSƥdi)n]^~^Oragi%lJr|yLvljq2׿v^>?MOsͯO.p5pܹY1R%<7VmDV"#s ǭ[nؒgMVUۛt04Q(0}Oeafz겅OG2BBѣБ},=$ ss{ =;ch"6޲;a?4sZd޹כhoGlYSrNݼ2ӿX"+k>6-c>[ 0V+@iS\z:YTMW"##=ً9wXIPSR*4vox߉c~m:!bGkgtqgu?p“N3dfٕ廷ogRepqF^:ƚd2 JBw⌎N.RQ,f;u;篛Vs%j 2o(ΗNܹ0_-9yV|FƧ:Os۱0,j_1CijUO'T m۰LVQrԋޭqqƽ* V+yةN.n+}JDl\w2?<\{[qJOD{MAʸ~,)Ew8k5"7In^@D <{^n{mp%dVJ2RRQQj!~d88m<(o=q͒yzw *t:"سB5ZZ۰B~nɈL#׫w 4GVn߭C[.%n7JgP}23Tcd,"*\ЭqS~d2Y>~ٸB{:zW10 O `Z Ʀ_JԪ5{w_(~гg^s0?ۛy8iT^3sJ9oT02HVnҲAޠZpԱ;(Jy!/b3+tOŮ}޾Aat<<+*7oGgUDZF"Δ㸜,"wEA.+*BRY(JoGQ@`L&d驉q&Eq|tdiiƊK@D7IS~y=V|wÆ/|H"#MlajpdrGXv=1\<6j͟1v`7$%(U QK%e?jEX VZA~KR'.[s'?Yʠ%9۸ť3u:];~UqOTpmCC{7lʽ-"y ’!Cllhٳ'o6 3{v%&fi%loO? &38]IMtq&[[2ӑ.?oہG|w1n/+# ޿u<3.liiED#d=E 뙒@D7U'^_io3iOy%'ٴd Yt䡂MZƛҪ[ S棫CnjP*ڣG)}7jBI?x`\Z9e +_0a:A rIL\w;l:y|Z5L? wr2ddFL_Wv=iɫ٧urRɗWSn#sy|q;a}iOEcM<կ{ȿj TR=Nhkv1k7GLwȸimַRVmr7vy㫕k5prqV3tGyxHMU<ٍZE;{G6r+>{+ v-iSƍM6gk>| -k;mZoUkiC/|ym??W{))yɶOFƄBHKa;{G((tukד+߽y#GEթɉo'Dms0C{y?VR[O<ƃjW G3q܍f;ߤk I~>vߏy~˄ cǚU֭dصkx-gPK)OmoXT3@!6hcg_WNGDA5B 9Vs"Jyǁ]wa \Z3OCtOniOG5hf\&7_Lg=wޛe0[QC¬m{yx[YZ( !a KDI B66v B672YWIy~IqNsr\lm~*-{{{߿¸i 7DF}8>##)+޾KzN7 ++dfqɄGQ5uR"6!;Q;QTXʻo۸~hN&;wg2ka%;D˗5""Vij \to{XLGg7 Ɨg}oU? 7XXϯS!]ZS ŸcEvTdH՛aRi޸yRCAY9?7|ݺ]"sa~)Q(쉣9Ygerw)[PasHXsrL `=N,Nr%"++gWtFdV1X]< B&$$Hcѩ8 /7;`G _tşd'yI|f8,bZV{^_ؓU۷'iưy'"\hEݬXYvu4 8v`qć_@"鴍eKbw)ⵋgXR'O*utqŅPn!ܽ|X2\Ua 93W.W\>sb߯?_kISTtNVw@(a=;~b3>+şMe ?|IP%H,qEke2SX9~+k"EkAl5K3Bm2Dƽ\&wwկӠY:9-ZW c, 81.+#_mt+"";{|Je[#oپۊEsrsRC7tq罙aT^`^N;}ێyNH9w|xOCǼoн ?/ "rrqhpO5<#N۠i;6хj]mu?^9頋 5=H4a }݆3*7rohwh ~)*7'{ E;de2ْ ǽI.HKYƕ?|IRr%ӿn $JI[wepR@Q6:@H&-۳C2`mVw؀`[hZ9||PݮID]f3(>;zΝ:VWɯ^;NDMZ_p 6+WrбV˽|*}1Sfe2٩?J%b`rß%)_p 9Zr\Ѭ~u?'4; FSRxA;/,B1{ljDu8Eɉy9u &oV,T-uQ { ^d2B""Qui_ea ZZ& jjT(Ӓ5jU858L(&#=Epvq43Ҙ\~^xs*6iߨF\ػ.S+@ xnď<6zƱ0Qu:B򭬿 רy %&MH>ԉ[EVkk`kPDxJ|ZEyyc2d?GLqFrKY ؁]) e(3-߶Wn} BE}4{VPQdYߗ< 5ܙ1^÷xa^E?%Ze)A"^#9Di /h*3:,{440Ѽ*B׺E)+ku¤zh4Wʨ,4Ghm (j(FE45{Ԫxۨ^}[W+F򫔉Hixe2)b P&)R&6"^ ʵ&6"F4 P#@yWrb#R^qOMlDJ#05w%$6ju»Hix啐؈Fxjx KllQ)/B/$&/B/F4db#RC`؈F0`؈Fxjxi'6" $6"^<^Rb#R$))@e&FQDJ#7hqHvxDZʯ~/yur_4i{n;vgl04x5"{Z|N#RU{}\8Fp#? 1K prZJcjWRwTp2Zt S K;&C3 1{Bhl;յ\;6G໯XDB@y^`:W eF(5@@BPj2P# eF(5@@BPj2P#\txtNJB!~B`5ϡ!^Ngn&VۛGӸ'+5Y^ý/"5  eF(5@@l̚5N:!!!>|IO_~ٳ'444,,l˖-oשSf͚EEExn{iڵSNF?@`&LqN駟K٢E B!Bdd$ڭz*<7l`rM*U*WܫWUSuVTTTiׯQbb".0(+۷o߽{{ԨQ޽nmNW?))ڵkyj+J''RڵkrGu̙s޸q#&&*W\z)StѠ:T¦t:݉'_zuaҥٳ=<AX?rhРA|իWO8QVz[@i={V3m߾N:}ݍ7ݻ-ZdC&ݽ{w|ԩf͚)S~~ѶmVZeooVPL&۳gOivFGmӦ +?SNh4|͇~<@e"bjj*q666l@ب*Uԭ[ѱ9s&..ã~nnnu233u:Rd9{lBBsݺuJsbccu:]jBBBJ8'Ok׎8,,,rssDQtvvV(YYYZV.;;;QVV˗ҥK驩(dO̓uٔJ۬Y3̝Nwpp U&;VVV&͛7 5޹s'>>>>>ֶvժU_|zz?jؒ] yxx4nήiiiя=*,,  -ٳg߿/*U$,o aaa.\06l3|"jrZ_ǻTV-00NQDdXԿ+ KgϞh4k6xX@4DQtww/KO>mpqqq駟X3m۴iӧOљ3g:tgddQDžXti^^M6M32V5jThhh&Mp"@)>r >2mz l9=^ZrMoݺŖL>=--q6}ԧNjuAN6l(#_#C7oP\7ςW8cƌ۷Kվ+V1>>>εkĿBo~7n^&O0v9ionٲqq7(AM??ׯKGrͧ^1\F)/Ν;‰'.d&M$ k2_7~:\.?~swwwoRWWWF__reRgP(RJ1cRQ r1og7o.-zJlK;&7jdIDTZ5.+!Ԩ߅\RIO@v~R6@MDG1hkڴi)7ׯ_wK)PH+WN4)//}تm۶Ifh̘1,}ҥAƍ*ݛŋ׸q㸸8FSTTtIKKK"JII`kջv""kkjժ%7|Z}btڕ}߿Zɹ}vpp0EGG Vf!o(~e+%%<<>^V':::--%ZD3>(**_YY@qrr"3g3gΜ7oޞ={?wݻ3g|뭷'L`05 Zl ^:jYy'N4/ړ_.ԩ[di-W'1c[5uTi[x R[/LϞ=+t:)zȲI}*)AWR{ KqX$ )M6;e1lw uTE8]VڵkYiiԟ4i[tlXΊ+ؒ 4wΝ; wJePY ֩SGZx)ǠիW&EV3CV~ٶ e):99?iV~[O|튋cY^^^[0( Rm՘n5Gʂz1[XfM)+\" c>쳑#G|# IDATǵ ƗxerY/+v199Ǎg..NF2`|Ν֭ce)Ԙsرן8q͛D!{N&x gC"1brH05 3@}s0=bR ^vΝ;QQQRjƍ`>}Uf}$"NV\Y4}LJ#7onܻw޽{ֆKV\cǎK:$sa` XAJe.\tH?9RWɅ}F>}fsj0Hi|. {BPtg8-Zϝ;'=%cǎd2777ߜ~g[[[pB6?5jlٲv˗j/jx)' ''rsssssK{Xq=W4cDz˖-[l_1l0VvTDQ߿3gZjerҐRt'hb޽[5o޼֭[lIvJ3bt_RFFFY"_r122rŊӦM39g(p~sɼś7o9CEFǻp† L9xΝ;Y{}IYCzI4Mvϟ/FOIT_rx#&LnjSj׮BDlJ5p@sW\i$;;[ M3%ݾ}{V0x׮]ۼy/{q|Pe9@QPJ\`u묭uwxZW[GjqZ@u=pDP3&?N|I>I9Qׇ5&'4۷jg -J]4F;YZͯj7n8#5U'n3f̮]rK/5^x_Njm7[y͛'۷o4;:99ɉ'N0ٚwk.((H^O> cǎxbӧO gȐ!CdZ;wsrʕ+W^5YM[oCoV.]͞={q'&''l޽{ݻwԩr1'`eeNڵ brU-ZxKnd -^{-v޽rJkv߾}kNZtڵk7]tiĈ?k=`Og8qb̘1Q޽;!!AqFV7rV5aؼy?hizѲS]R$F k֬s5k&wttt|O>Zt>0~Ɲ׿Խv:cƌ~UVj5kCo6i޼Q䦀  SѺu)SW9o<~h|8@M֯_o#9sI=eϜ0)**Jez꫃"o׮]q S>LX[[\dpG%%%WrBCC N1߽{U *Fc:nҤIe˖#?p{UШ`^zllI&M?eh0Wi=j4 W-~͛(1jT޽{܄ r|+"f֬YSNݰaCXX؝;w]]]  MfeeڵԬY_~ݷnJMMZjj^uB?!!RBjժM>]՚TiӦ_5:::33nݺ&L0F7yrz]߽{o=v\q&LP{QϞ=;""L2={T7͙3gܸqW>zh\\\.aÆAAA*ժ5jܻwoɒ%ݫPB͚5|||BCCu:С`P/Ok׎7oޅ ݻ5vXwww!ի5kv̙rթS4iҝ;wLh)GrcǎZ[[9d;lٲ{HN>pΝ; !ZjqƔjժd?-[nݺ(J۷oߘӧk4:OO|W . >ydtt۷<<<ԩ3r$[g(?j֬i{ݺuϟOKKZjvFmmm=}D5Lڷooee//+2eJlluG:YxJ*)ýGN?W3&&fɒ%'NxaJǍWj],ѣGkN8q:c=+W~˖-+W6lW҄ bŊ+&&|j߿AYS :))5w@qϧmBL}~MwwhFcڨQ#yb8h,[XXQ# j`D,5F@XQ# j`D,5F@(ŗNoq7A(%gD&ee7ٖrhGXQ#ŘV,Xבw7i+df~ӜD,(7+FXuZi5mTѣ"u`qDou_nYI}(gbor̜hοQz5gbc|LldJ#g`R>&623E@I؉Li5PB=SLvY+i(ʹqiI:Ø\F@YUlo$+;֪DΈgeaI}t|'%<[DŒwJu]9I*M^BL5WȔFF*҈AP=vb#SQhNldJ# Q#@F405oyLlϔ,ݲgө$Q#@Ni-q?7- 9(OɊwrb㙈ƝyLiҭ]2E@PL3ӝVÚ۔,ݚW CF2FXQ#@I]˙kOl4Ҙ[;|NBF<+D%>Jc2hFuJ#!# Q#@1ϐٿLCȈG,ԕXA\Yi5k;qEWua>TJ8򒕨%XdG_wJeû6=m0 ^jV<ޫxJF`yR^ ]uao3uب7u8/[2m^FC J21|9`LFe< F#pD j(, ϓ`عsFT/&a] h OD޽{xbrr`P[ͽcNII޽e˖g3g C͕\kyڵx!MƍB=ztرgyzz֫Wy666&"""8`hԨ]HHHȵk233ԩӪUz%I&xzDȑ#'N믿rkE)h4zU?x@l>`׮]]vy=1--m ,0鷱yM6;68p_6|ϟoٶm۟ё(yq/tzN"j{ׯ2dzCDEEedd!bbb###gΜY|r/^hW^kmm-9Ni P2ؔюۄ:, xKLLTsݻ={6++ڵkW\IOO?<=Y1k֬SNǯjǏ?v؞={tȑv\r1cȜyƍ!>>>;;{ԩr֭[CCCɲ~99s挟]|2gtuu=rÇݻwڵk !"##qƬF@l2xWvaA;vׯ?,>};}t֭ bƌӦM+ jժUgϞMʶVO׬Y#X~}۶mGɆ 󖕕%V=~:ôVZaaaUVB[T(ە+WdW_m_|qݦM)]v[ӱ˗wwwWLR\9.\XxqTTTTTTZZZ5իآE ;vشiӭ[E]ϕ=z׻krovΝW\wpppwwo׮ݤI*T``ٹwXRF 8o߾?G1 CСr IDATAƏommmggh"Y-[!uֿ9,44433Sg 1BF7nP;ը1Xܼysjj⭷29ŋ5k S2SfEAN4IF/ oFFF֭sXL}ݻwnOo<2>>#K,QG&%%΀#DDDTR%E 1tRiϞ=mڴ1ݻƻM00_tϟ_jUZZڦMM'=zysСaaa7oB7aÆB&MzÇoҤIDDDHH'اO3gx{{eʔ &\t}ջ|ŋ>|8eʔvnIIIBL81FDDjJ_zW_}ٳ .LLL f%yہƝ7o\jȑ#5k&WVt'O !4M=̏ٯ_?8xlTVM%ҥ˰a5BMnݪo7j(s匌 !Dz/(Jndޤ^zl~w111Qj 2D>Ycl߿_6ԩӬY3!DLL̡C[N6Ǝ/`ѣѣGk׮5G5mڴKN2A۷eQQQM4ٽ{۷O>GMה)SyZr?Ê+~]v۷ҥKǏׯs[nS/_,7UXqƍrec q ;;vBӧW ֭[N$\\\:ѹs իgϞ9Rmŋz(:<{J55k4.ͷ !:u$cľ}>}:K^->>^ UVVV+W߽{jժB;vLdNe+ZpԩSVV_???Fj.\ϝ;s2>  vժUkܸq˖-ehѢsqrC(94MϞ={)HOO?x ҄[PP\YP 1)oӦM}e˄qqqK.>o^;wl׮\ϟEݻĉ۷o=sSSSM6=Algggұc!Cucڴisʕ׿V5 2jddd[nʕTw7|ի2!mԨё#G?cپ}{ժUSSSݻw̙ƍ/_^ߛҽ{VZK^ݻW%MNVX:x`ٹfQt:ud58ZtixxxPP:o6hΝ;jg ]vOpg0BBB:t0`'Ja eee嶯I͏uܹ; !6l(HHHhѢE> *Q4u(#Gm5ȓkOڪ'^~}"0ٳfhooߩS'>|.VujcZdX9"j|M43g!-ZDEEEnܹƇ]p6m/PTuyYYYǎ9sf:udM>cP4!'i !~j)"oܸѡC.\Gbbb4h^pժUBs !2ߩS'y~q'{brZhr+grD(~3goyT/>'PVH.ڵkO<944rZܙ3gM͚5ի2kB,Y$#Kܾ};7nܐ OOO!…իWխ[^B>|8..~'NA=SN}yMprr ___MeC䈨P-]SNsylh.,vttR&ݻw=㞇/'-hРz׮]#G6lXV)))Mѣjel׮]!eY/-[dꫯ8sQx 0al_^Fuر`BlݺU=#>GϯV(s1ߚ.^J{V 5իW_{܆M>ʕ+B٩oңGWWWOOO9 rΝ;wްa`*ʕBjãe˖&nݺ%leeս{w69}[,O‚OO>gϖ)Sɦ_p}uea"""4hjͧC6L>ٳg˙cN:uݻwL"?~^~/}jUƍKJJBT^]]{ĈpB˅6l8}V]A@X >s^vmժUg̘K.]v?XhQ?9O>+ !N*ڼy .Ǐj꯿Bxzzɏꤼϛ7ObLL̬Yz27nڠA`u /;**JѥK<0.Xޓ+W_}Un7o޳+3#=zW7͟?_6^}չsP/##cꤿΝ;PlWFBQF_c.\ h4an.>39y2CLNNnذ .\p۷7klŊ|z+VW4 6o^z̙S3(gΜ)O-+CbJFpP,|X]v[oݺvXUN/1'$|hW<813gΔz-s_MZZÇUVco{Wr߳gO>&ΝۨQ#[[[OO!C$%% Ĕ/ =~8A~ӬaV#o޼.ZbB .]1cɦ_~}5鷷/_qB)SŠAl۶m޼ylll{7nh4.t#x㍰-Zq۶mSJy϶K6ߗ"++˼ӘF +(Urjb1?z(&&Çz޾z*y|r\\[*Uʖ-[F 8\BBדjԨ!/tX =rpp4ΞD777;;<geeۧ{xxDFFڃLLL+hXTTԝ;w4[|oߖRJ###ko/4hmK/ŋ)ONJJGG'S ɛVUg=CӦO'[[[u!2Bwy0wvqwwWs'OB+͘1?޼yW0]C>,<<~~~T (=8H111u (m@;wNQnSN͞={K,Bl۶ͺob EQ޽o>*DEBjرc+Vam#G=Zqq*DjԨ!{qgӦM>7}}}111 (U@8::/S;G֔ (U@!Zn'{ʖ-+"XrN: (U((6m,_|ѮÆ _W~q;Q{###;vV@B M6Bt~A_ =j(OOOj*@ <<\挹\`˩P0@rr{UVhSRjsھ@҉@5:t0_%۠t(@ f\\lmmm[)KRXDDĴi6oޜeoSOM҆+ްDTNO)ޠ(B(j2 ÁڶmM6mڴ~ V@BՈ &Se[XD'?/\)G6ԩӧOWWW[w~ y!Ĉ#zQR%|ޮ]P8Ԩbokïx矏=B|wAAAƛR3wU17##cʕJ %wM!I(egg5lBDGGS.T!jP|y!D͚5SN! JFPriǏ1BFQ.T!jѾ}|VkuĉE9;;]J6E;mgS7j()+[(" Xk +CEJNO۲V:קn@GEK\(ir,]~F bxrpT j+iQ _prp j+iQD@BEc|QFB4lذm۶=/w7nݻǏ79udcرE8P5ύNk޼yttV?֭+'N8w*ؼy?,bŋBԨQEŢ DsӡC .!BCC6m*;7nx!Ē%K|||d(k֬sehoo߯_?!ڵku:zPQ4 p#jD1PiEm<B=9rX6 d(B۷OBNvիW s׷1%=KM@ (ZYin;yS(|IGɑmgcUtEQN_BdfZz!ĸ'Uh~Bjت^3>Bk읬+Uy;)>ٺl+ B4<0' IDAT XWVNVC=]͞7?Q#% ՝dA jU @Qsyo?HxS:qB&c3B˾pxGnܦVZ oMpk9W >n\y!'ۻH=U]ji0ۼ{@ۺm@qAYիWu֜9k׾m۶je˗/6?^"cǍpT!o|Ragg׫wI5yP5MQ;w%$$+**QnR"33o߾=C>}BBBVZ駟 52P, P*Wܹsg;;m۶ !:unKQ'''_td ӥ'N(}mΝ;'?~|1`p+f{O@F6W?#!g̘!;ecҤI&;ofĈ&B !6mZNRBFJFyO|}}k2do֬ٺuV^uY!D>}L"1 cƌ)Ϛk5Ghl׮]={y󦣣?rC^hQo%_Ə?}t5jTd"j OϞ=TRBΊ+o{B>}m۶>~ڵk###u:WPPPJTr:ں>yE8ؕTD@ҥK.]L:sح[޿_U4&"j䠠 !F㴵%b)%%_~}g}'X|9eQ#P,ݼysРAuyyyQ5RŊ \\\&MTNj/FXrqqYnuExzD,5F@XQ# j`D,5F@XQ# j`D,5+Jxڿ z^0EQ4mePz`06R(L$Fs:s"}_-;y ?|hu.;o7 Ν:_(^9z 8LQ +)J%f5 (!JcÄZ{``nY7r⇆Nx4RރX٠Wvfcem(ߩkgBliUɆ3P(MQ_7?{x:%05%ޝ-v2?B-^X8K.u+[?fffƑ{ڕup\ŠB(mii)}ͻn6rO:veg?=*f7gwlqPx! 6-_>V/f5(jOdn|F9Ǟs'޽USivv_vWzsrun!V=\9:%J!F@u# !D2e~ccEx޴_6(ضq <$]!M:.iVeFkA_4k+27dY@rvjP$(JA.@9-njam]F]{YY9ifFz~CmWiV{E$ JHA VzI xas E@A"Ez3@BIBzHq~kw0sLfgsF }|"gP05rfZ9!UQ[vKfB3_:x{{ 'mwB\ROO/o~jKϛRׯ^mޞxI649g !DC+WNY22p·B-?hY0?m\5|ݞ8ߛ:.P!ߧ:v߷k|љ>ַOIƞP`Q <<8bxzzmZE9پ&Yqn^W4))Ij`fu~ߥY_ nӾ: 3(Ȩj%^Iio8ΪH&a:=O*dŒѴ-vZm|`{OOωS?yiɉe=6Yﺱaq1n2 (T(ilЈ74fZ| |kPPǖ>>U\f] j%>^ûa=r {շXӴ6zVZþoȉbZ {]мuaȨQ#50Q#50Q#50Q#50Q#50Q#50Q#50Q#50Q#50Q#50Q#50Q#x ܏lٴ^y/:f,Stk/Tj߉Nxg״u+ !S"#n arcyy.YjZmlG^L 6U}v`d6wE^uxV>qUٻf*v' Q0M||[_hs3G/u:xv̔68/g2̱K׬]4-O1}ؼ_iBϏҲcfs인㿟qn!v[Jb; ZmG?ӹokxx?ƕ[BקXRYRMӼ}s7g'FĽپvߍ\`Ko"mpWDn"Nwӡ[B4_hM;~y7.%mnlPtp%OQXa9]fH\tEBeՂ?YKzۚ_9x|Obj؊fӪ6MQT} %Ku>O/kGac+A=?cg 63 -뫓W[]f%r`Mx!D$6"jt('JkҦ%⢱tB̐f9]lq6jwp rAZV5OcD߾'F;13LIr|2M7a0fd6tp9AԘL9_hο?95s4!".G{K->tBrF7FԘiB)-2r ܋Ag1d2QcٴR1Vlο(/twb_B*[|橅|}l6[ 5r<!hRW,B?tb!*T氞%{Q[gX;6 | l;}5yW*Uԩ#Xf"---<<\ꫯMϰ_~˜ޟ֦B/D@6tP!Į]RRRV^-gc!#nӆ &'emcv26;usUVڵK1|ph>F {w){i0`@~D%F ߿VbРA&)~BFQ#WOXLh_>(kyt+sv$p\tڴ/5oZ,k_.y1DJZF_/LJ+UdћYun<|5`=OV *װz8]8sӊ y{tjV8ݫW(Oe^~x}ӥIivs,V/6c!pP0qOӻjb msYiuKm Yia²~V%rc8x(0X`oVUظ{eJҐ=TC6cMBYJsr8x(0^ )iD>4(9<,9D9y<4D/4"bC\4('l uJ<1䖬9D9-<(Dd6voQ,%WQr4('⢰FS..iQrOQ#Ea#%ȧ\9D9u9D9u<Dd#4"_s /,!A^@A@6rZHI#5iQ4(yOQ#ˮF4(ycC'eWHI#܀]C}>h>?x'\4>sŋݻWD5jԨQC4555))IQhB 9}bQc;vءC֭[sFeȑK.8p`R>}ܹSq`!O !&L0|nݺfw}wNOOMzߥKիW/^ܮٳO~}̪U.Y駟lԨ˗K*u[n !y-[T//Ç˼ةwމ,Ŝ /_L0Aѻwou^LLUfj d3 -_\NtڵN:Ν2e4T%&INɉ˗/׭[W挾I̙##֭[W挾 onwCSNV*_t}+V7o)##cȑ2gԿc~ˬQF)sFɤ}=ZNlܸ155y-[5s yB5M222N8f/no!'T5… hBNDFFʉ;vuV~}3gΔ"Rϗ_~Y _SpYfȐ!rH"vCعxkׄ5j۷M69͛7T̙3S!ҥKwO@Ԙ5mTNz7;UT*ՠ"hٲ3)RB BHc/BܲsQ ͚5ȬSO=崁99 X+Qc7 !}5k:ThfH=Qn߾-'̙ 44벁*Tr3_ =ydrW\ܹsf-[vջ{n͛rB ,_~  d7"'N>m3;',U&&&ʉ"""T˄rJ:u8 {kN'''gq޻wONOa9SŎ 쨞d>|x۶m3kf*Vh7l./?xhhO?Ծ}{!_|QfMǁhB޽{'4M?^vW_=sB˗ !ʔ)ӦM@ j}}ݶm?1cW\ի״idZTT.: ̊9QrAH_=OrqU^f@9QL-ZDDDl߾=99ڵkbѬ3: 0@NŽ?Nӧɓϟw)+(E&=9fEÆ {ƍSR?3jtI&rĉ@rŊ_&jk V~ڵk6wrjժzխ[W^:tP~}5שSg Xy׭[^zWf UgԩӧOB̞=r#FpڲGr%Jd%jLKKyM6 i=,Ujժ?xv }z۷^zl_.]zĉjjŋj56m;==NSc_y啀޽{jJ?ԨQobݺuNB<s̼2DVC?ЩSMv .!ʖ-[ZgvGZn-5j\tiС}vL@CVESNVuaaa|nݺZO?AY={Lm۶+V̮isݴiݻg6kժU^YfUP!_ތ%Klٲȑ#111rO<ѼyQF5n8o󨨨'O !޽c/ڱcG//ŋnɓwxؗtرc߿oX\GZ??/(RH.]{F9m0y}:ujԨQk׮~#\7.+7<7oA 'N|xf)A3{;v8]_rJ*ׯL29Ξ=kxݻW`ܜ>[,g^VZjıʕ+3uhѢcg  .|뭷]ʗۼyENy{{ׄ )_~ժUkl=uݼyXy/bVq%''ػRZo7oLn~؄<&s9Ƽ:t?ժUKIIlޥK}]fJ׮]ϟ?oUըUU!C#vʗ/owTwZըrW5jvr?ѣoݺUX1OX\9,TPըPUɓ';Ll}9V(HHHȬ֭[-j"ǪFMWzIIIQ+ZLL~{U5:=モo<~Uc޽lݺӦMS?66]Qˑ#GL&*VN'%% ,/2e竿aÆ'''*UJ.;wc<\d|#GW^u,iٲcƍ]s9rdذaz V\ʕ+B/Rߙޗ_~)vOjjfsٲe[liWgX^'x"(((ƱbŊ5ktq4ٳg#""bbbBCC\wwr5lP{Y,///7ߺuرcw)RHhhC믏5Jh";\BzJklh{ꕖ&ѣǏ?@un㤗_~W^lwMLL|ʹeΝr">N}C !z˱ͱcǮ]XN}Q#yJUZ5j8=eqNKKKHHBxyy-l{dddjjj``]b&$$>|͛%JhР#ts2eԫW/8884dɒB+Wh|^xr*t~zMӄ7.|۷ۮ];!DddhѢJ@y3g+";WUխXNHHo/^*&&f9X,&hѢ Uw4->|U!e5^pA)]>JNNV !K,O XK%J'D1..N_UX1ӧOvzz]=z^!v#V\Y_֪U+U4j4M_Ofr;}TTT"\*ʱ "oݺ5"[/>~Ԩ'v IDATuQZ~._\خRn~ڱը)K.U3ݻ///nR>s{ߨQ#奿wt.߹zUVY]ئMUuL:jTdNsΩRY٬?l߯_?s^%#׫WZjTlG%v5C~Էܳgw"j*TȮ.m|㰊. :jtmĉvy䔀Q/+QRHݻwwzs<Ta6oެ.9N.vן5+VʐՂ ;V U8\DԈ\A;=QRFdlVv*h޼UvqUML8QW黊S߼yٳgU8t\Kէm*_j@YdQ4u=,6##DrCur jݻ,ZjiԨhժս{[n=䓎uF֭[߸qC噱Yrex,**J-.TMP:uY/תUKmoo]vəսQ]8*U=Yx;4*ҫm4mT6f5޽[η"k\xQ?ٳSSzwܩGrd3U9L…?^zuH !P;ӨQ-e˖UfSڲNfToDT3U&ID֧O}!U5q5XiZlle+W̙PWpa5S=]5Jv޽N}B"EA{.޼Qb'p2jt-jT DzYܹxL-^?:.Cw޽+߆iiir7e3Y>P'?'NPSWU߮];޽[?FQc."jD jtÝQ)Qرcׯ!<<<^E=,:iԨ"y;w jڴ:z}cuk%97mG111111+ޚ__|q[n9sQ1u|95j&OOO{BmQ*\l6U)~SqyŊkذ5Of͚9ի:MTs~X#a;j? SU7#F{[nϝF5o\.rgb6MNRϟ/d5j_Ge˖7oO?b mR=Ɣ"֭[O<_}f9eT'1\o9jܰ߮q\\dQƌ3v֜Ej\///X xĉ4jTsOCNƤ$+Z,uԨ"iӦ9.UY)ɯo)+rSU3F\x>5N?6**Jp]D 6̬hZE0Ӥ=k w|=B8tP.]퓹~J@ԨqɲMÆ L hiEUjiꫴhѢ)ٳ3XVՃ#<v}=y9\rvU/ D wz9QV0F-k0ǢFDZ,IyV>Wc1}vTT?/_4jtܹsǶ^؛!r,j?emT\);ŷU>4iܹrJu.=5$uW\fW^Q33zl=OG:jTc;~6ׯru ݏ&g|Xn)#ѢSc~J@Ԩzjqծ9==͛Gq2{l5Smt}Ԫ/xtU%.MwK]lgΜqY'fwS¤ILU5"F䊬|L2ޤI7]SL>}?n`,GQyTXoU]so߾G<5Pw^9bI^M"E,c6Gr\?͢e˖믎oܸ1|9p{xWWD7Wj*]rX;_m4Sӑ>)11144tݺuwxf+V111wQUjU1DFFa+V4Sl6~\x_ֆަMTU^Vb'NٳgРAG2eJPPU)\v&"E{"##eEWnڡC}x?Y߱R ۷w\ZzuynUf%wZ.]$'j׮8vj֭o vQv7ߡCU,8qbf87.X@yG U{yyj~67n8ӮчSBMk%>>>66Ν;ߖ}ݻw* )A2cƌ:]tUl~d~ZՆ?Fm6[BBKUɪngwrJ9BG}:}Z ؕM?+Μ9StiӦAqPl&IΝ;FQ )A>RV{C ;w,\n߻{nPPĉ/ H}k 냏6w3K:ut_Ɲ.u @dp-W\xxx\\?}m۶ !bcc{`[D^anժU``ݻwe<&?UBki$ߣ)4#xwBEԩS@@@\\Yf%&&ʾ^|.]VF//>l[l ߾}7V?ܷoߎ;T㰰}EEE[n׮];vĵkך5kq^z=k׮!ڵzm!BBBfϞݾ}{ƍ>ed9d2nRh ԱWh-ZHNȳÇ˻>=P{yFɓ'cmHӸhKess޽{'Lp!"Zre.]vуI i* [XXdff##8DīU Ydee F>'wz%QF5j̛7/''gÆ ӦMh4cǎݱcKlKtJo"~2BP/j6669ǏolْΞ=rLg.w҅7n$ׯZQlx8bvM.))N:1׏2n~$()  xم ^ݽE{[<=={G}Tpv0?c?z111l uZ7nاO-..NbV&P.] HxGԯ_߾}|… F^qȑla_T"M"?=y_wyf~QKN n%*n߾%q+W%ՙ\$4>ײe˳gϲ< 1O6:;J %d_9tPۈ0awܙ}fo._d̈́XC/o߾^^^|U;PBK033^:5+5~MJ.q}wXZc>;_S9 ԕ+WֹO*lڴ8e"##مիW/6$:jnnn(ӧOM6[?lo #9##VZlȑ#_k ILL V.-Sɓ'O8!=C/|rQRWZEL٢(~Wώ;J |{FJ!Ez)p[YYTt=rbff&ZWfM|E~onѢE6m6N:/gkD m|jQ. Qߊu_6mZl_l;[d 0&&7~gk)Mg㓋/^}?ѱDZK'ӋyS _0q*yVZw;D4s̗>8`6m4o<6678ooo{ *w#*PKu[lywWWWgΜ/:fذas̙+󰝣Ν;i$gggvYrr2;ϳc'Ν;wԩ{GQOQ˨Q'|2gAIB!?vX233c"*͚53s1\4#yRJ!CLͥ7n|%᥽O!!!ѣЃYey"8p U`ڷo?cƌAd3-AӧOǎYJ*f͚1cl,Slmm.\(/qb=I =5J\QEys̙!!!}-^5g9uo\?(t N? &:ɓ'g族5jܹCe~DoV\ٳg(J,hҤɤIx&ܻ`ʯ7ȱchW_*꧔wBV 6͛7IvĈQQQҽaaa|& MZ3FYCɓ_Q1 IDAT?_n3g~ ꯯wbQU;w~(7&(Jb7}MEܹsێԊ+Q͛߂߭[wo>(J&: 5lٲ&32D`凤h~dtt4"mNOz B]~=33S$U~w9JݻwSjUӚ5Ș+W7\ 5J0`@~ǯjEʕ+M։ܫYoPIHii)OOOݻ}KKUV6jEqExT3&ؿ `QEiwljBO:|}}W13''GzdS\ }^,{e Znjo޼ŋlY+qǎcGNNN*d2YFt.䋧8;;\)Å(J eoo~}ݾ};%%ܼ\r^^^#G=zdw4h JI-YԩS cƌ  T/`~!-nܹSLYbEtttdddzzz}||ڵkg=%ix{{k4 ]E&ѣvSN|d#FH[27n;wM^h=<>>qqq;v8rHDDDZZߘ1cR̤IZjzpZܪU$Eŕ-[g߿C6lťgϞ|mGիO_t)!!#((hذa4aÆ$5EsA>__ٳgr&5cǎjZuNNN~~~ÇNu,Zhyʕ}]|999955޾B ͚5IEEEٳ?~ZZguQΝ333׮]{̙<}ۻz#G ?6mʾI$6m$%%]ѣ999...u8p`rt۴iRSS ><<\ȥV&JU<pJJ (cl dz7mtׯ+ oo޽{oٳgsRy޽?<&&חmhBPhZŔ'MWp3f꼓2zjԨѢE"""222\\\ׯ?bCq… ߿C6E=O:599YV'[OM߿3 :#$(q8jsoÓk׮=j(~ر_4ԩV4/t8a„G鿂,Y2yu]|{zz6o|:o%2k֬9y ̃>~޽lٲ샐L&;}Ν;wCAu֭[ǏϞ=[&鯊;|JeZE t,qq|گ[jrIN8Ǐ={v͚56mʗ[2eJrrL&ӟiӦ)`ŋ/]R,Ybkkx+Wر#))I& 6LTZuvT*ŋ\z^z|'O3FTZYY UTNNNSN>}:F_=k؟8qM+ݵcfvzi999ׯ;vRlذe˖ݾ};11ťaÆ3g?g %;/}ܭA^9szvo4icJeP O`<{VsFS짻̲eJ(ض#-ʕ(}&JL&Mx9N.44d8cRRCg,X`kko>Iղiٲ%5>}u֬Z(ɓׯoڴv'NSKoii_0k-[.\PZ;s͚5D? 59r`?T*t1112111.]Zl޽{ye@iW_|Ҥ~lH9s|QFcuy)1c'N o^ZPj9r$11gϞ)-Zf xp⫯Zh|h(6=믿VT111ݻwVYU~gϞꬉ9r+~8}jժEFF^paӦM|ykk!C( ٳgH77˗>?~ڵkGFF`Pʌ7nɒ%\ŋKf2P-X`7o n߾&www{bBCC;wleeUrQFmٲٳg2:uFFF]NNNNgiiɣQ"6k֬Yr%+ic,)o׮]algg;Ҝի:t`޼y̙3?_^{ŋYQP޽ d5eݻw{)Sv/u֭[Xe˖g6VI8pmWnݚmڎ;/o'N%,ŬzyfV~]|&8p@Kڵk6nܸ(lllɓӧO?}.((iӦq098߿J իW,pƷ|;vڵk:t=СC6lիQ 6Ņuww/Yˋ-0a͛hʕ<(֭[O}q''' A([,(ܺvvx[pAH¢O>l#Glڴi֭l/3{0x7nܸ#SSS fݛU/?ߩlԨQr߿%~vܩjh̘1WE`tPcFF3z3#{{.]t_m۶cLjhΜ9o ȗ0Juݻϟ m3|M@RYZ{p„ SL!%K.ׯg 1477ϯs_ z95 G:::&''ݻw +VE :o?g]l/uNRݺu3zhj_X]vQǎgx;P!ǏӧOڵ 8z,111PcDDī<*6oM&>|.e,kk={nݺ5>>ٳ Ξ&'OΝ;lBo̙3Y+W٬9upsscl#33o3~GK]oٲqƍJ*{:uj\\\~OUR>|(lvp)3qDV~ '";;wѣGu߽{mx{{G@q [駟P}f͚_<_~j}||؅QQQDtС;߿_BՉ?RRR"##7nطo_޻wVZFb vVt JZjh…~xΝ;w5y*U?;Pl!-_/]Էo2euuu7n\ll!Ct.y&bc㏁?322.FߒU;6V7m$p1u]~e^CyYhpSwb#xƎkdE~(0Gx/3k,4zj0'$$;w/&/-B4wܿ١C4 0F -ZgϞŬZBV#)mذ7yCgj@LF0 kP b@.C/Q-)PLefRS L!G;Wy-P`ߎKyVB&;}{/͕ @HfJyZSB*OA Ί zhڢ3[kg9UT2 BUݵym,*4ɴSHTUE{HJ\)]JP{Z[*f&4Z_K%ks,|),m+[w\߽6:4ABnQD#H 4]U3Ef=hEaI=Ѡj<50#;!Գy9 Rl[1`Ks@akRóٽ_ N` K;k-T+8t4GƘ^r凙F+zF+NQ%eT C1239q>ȏ9ʁWadb\P" B1&39q>(X9 %qT @D*439q>(X9ʁWWhb#R PcTpfr}`rs&f%6"JPcIU@fr}`rs,P@b#R PcI_fr}`<9,+Y9`a؈F('O0`fr}`<92؈F(p Xg!'ExML؈F('OЇPcɦ8tNnruIlDJ#8yA5l8ġdkf,)PF5Nod!'/ +^؈F(5p}5xvfZTDN^uUdf9j Rj<@J&(Fv4G.?Ak8}ZVJ{˜-Ҥz^z6հNu^B.J'B ":4}nf PB" d;4v(MjZ`5 &P#B`5 &P#B`5 &P#B`5 &P#B`5 &P#B`5 &P#B`5 &P#B`5 &P#B`5 ($& %IZʢPc߯.Ң)=rG~-+)PcD29C"cX|4`$ !D"9 &P#B`( ʝVBKrdDiE҈/D ֐4"8łB @jՊ TZ $D ;HCTCN6" )ZE I .H!#dDVh^Ar/&tJVfդK^2Pb YٸKn\HzAD>NVmKOR!Pk;?|. roxRpJNEElݖl%T߮zE5S`aqϲ4Dlѹ ռܭ <! ߸q'^Sv|yi='LJ,Պn.6wɅirv4"p ǿpqZڽ1ct 8 t'*}g糲4jx|g|P_ [!Q@[@v[Ԑ|9A#W0A : =p j;R0jјsW9,S,_ IDATQ[[ +Iܫ[ñO'bpOʮe?T~-@vF--O'>}Rk~x2a쀀⓲u"[+ {3߼gGE;Uy!$ ?㌃>[1G={9iӞ{DtVJGnkmlv ܌Hm7H~Tx4)۪qYKsIm"{vFq~}>hsrbyf]#'6.Ӱc%, X0d5@ióGp@2!vJؔ2fI{Ϋ*6£+öOnm=P\^N]Wz( !06"kꦹ#P|BvR4콎{^Ig`V{Kɗk?r+[}t8wdkkܱcNc|,Kc]e ]Ԩ_w܀L8w9^lוWt(ZLllK)+!)'f'fefii:>)+.1KҊ [}߃7SHGWY:1uӋQ-7?Xl|R ٶ}:)O|ViWmGՊ w̱UkT,5~;BA"bre"5b˦{N_H0)^JDvfG6jnPoybZNg]o3Py1C+֫HD7"R/]~|!dm)x&e@nVJLQjd@A8}>~lT^˿Wł_nѺ&dOU*QJ;rƙvDIogxoUנ#aH|*%Tx@@̓́V5+;@D ?aO4zw,gf.߰W.lXC"""矘Q!g=ɔ^ܡϟ\X{;5uI*mbqIC*.="G%G;ѿѳέID ߘ*cf]3{\VIf8o ]I$2;wO 5s5z֙u.Yaٗqy ܪTy:n*މN'W' RÚ}'OOUDO?kSج(.]}ܛYJZw;-K2nGr s-*t39!9;=9ֺOPѳJ.] e-. syVz;DшW\&.Y=mW ls#³BD$(Q|Rv~Noo /GO\֑2juؗ`l6 3|3чw,oҭjI&@Vɔ YlтH.V Ԣ 5^4w"!Qݱ\NH B|3TUHh܃D695ѺY~krdٸ shx=d{]ZyS @L>~9ٽ7IN<ŶuɾхicCDiǜsls7~ZNDٮM}= wy1FbT䛽n1HؓK7H.7nHk(geͽmZϥ[ il5b_{l!܍NqgθS?|M݉is[I$ s]rBB#Ona;$mGcټN-=͈DE^[TPh 1#~uM~bTy "R: ;OwM s{={t*Kh0wh,K|QglW;/#oآ! ܸ@W'osJY:F>9k+Ej {׿ +mØ[bLa^vܶ~y[YZg 8xV$"(!9;":Qn$Ecշj-ui1fgYCYqW_OhODfy1a|Bz]zHX{BV$zs_ PBRn:W^V D&>_|m0ΤH+RƩߚ{5$zxjDDj-hn&'R=T{7Q\O uy@$+HТZEV#5i4ȴ@?[ D"kj={N*:Yۑ ͭ+}蟓34+7ܙ<iy9Ue+>ouQV#b1gRO3bnwn ~^ZAo2" |q|&}̽\9I>r>W}< >5BTTjmLx7A5># ?(pf O7/G ND;?`Fikޤ/,ѲhTԳTW\z禐Ms8"*kpFB+,?Kӳ-E&)e w}RG{3>W7ᕞH7 x"ZzdVdsvvtkk ml:=":x1O3@'*FrWc"ȣɣ*-Ϥ[[NmS~)GC"/CԅzYYPyp7Z[.,ZumDdcXaqF"-F vi`5Z[xG?OsQlV-'+Բۼr+E'agFDTP5G~پ+=zz>fmZ+omefVA[OϬmqŗ=`\TSD-EֻQ+nsIN>,ᘴ5bXɗQ}*F. \uuh:UsvdZA1#Ym8(wRI>F&Tjd"uIcym)35lme_ xZ7<_\HF?։bZlSk+xYvF'ldN/ƝxQ+|f /AFpYDTmmF]S$畚v;@X"*tZjY -6_qךfBZ҈5*;]Ljwlj[,% @\+gio[k"N5\΍T*0%^_]d"=ؤNeiyuOI"qZ5ɭrx,~;%%-q.xѺs;߹7ԅ_[>ʭ#X>_G]|,5 zD*)0=M#֬6yhU`HDj8{U:9ϼ=O<)"R*dgw#1ֶ+hs4w4z=^fwJ=2Xt"plԽVkvʍqty,%-N]O5Y?c"j-dZ-RȞ?#?Ii׾ն>|J瓪C<}Hf򥿅+lW,}<0mN_pµdRHH)۵BD]Zy E88CwGu  )ܙJ7?^NDv6r,֔r+e$Xǖt#EWL{; ]KևkMRL@s j\pj=:*kEDOknLr53C~Nuvdڐa}.*Gehd_[隕/4TD\ſ,Jۢ/̘#+8AɃtraVmMtwJ)z;w:ɭENOh }< Dgɶ-M깸:Y;]JĬGO22A?e_Z!keńBK;V6aYf?4uo}egrɇOgmnowo{v*_7?Df2gL?oך: 4 ?MDtU@Ve씧'<[xF"md j;qu6~viŭ?5ոK gK7˝W{3uBZQ^a1G.Ww}\|^mWFAY9=yUm//;Ï.Ar)Let0&Rk7zX&oN"r =V27&y^F6E*m-˒HQDjqfrMJx갍8U&/:{ 1Pt9ڛ͙Pm@.F\R![{1v67, /.~oiıgl WV>0 4SḿZQ-:h,)䲵 wneԊu*M-; =rcQ#aOH zjUx^ĘxJٷB.^OI70p'A۟͐ v1yWigrk3 ~Fў8#5vH>Cj\Uz?yɓIq]}BjeMF){V{Wא, sq?ҪУ ٩*鋺Jg$"j .-AL&ӫ"-X,˺r@F\e+ufJ|RͪyC|e2Az-?oeBNoE# ݷuZ`ޡG:$b2JQHF#(uT2!W&ysh#l5[#O˴TxY~ _j ZPi5Wmi Z,m_hUs#9\SJT22Q9?IEDH=CODQS/RADV eC{H$t2{)emk[yw{~|rE+ϝ*MK[B !wrӼq{}̉j}'Gm状^,Ԫƹkc5-f51R )LfwSu*1 _l8IDAT Vic({7q ѧVd9uk\W.MB R0db{V"X9L= 1bHiq)BCӝ$ÚR(C)M3ƝvCմ1ԞսG֮nٽQi^epqӕθu 'k)8}@Ә)iʣzJ4͇kFg&Kjۚ>GxS aw@U=~G  Pu7^VLejj˒[W8ًkԳeu3ܷގk,sб;wtn7`rW#P th{*~i_*1ܓ+{!s#g.u]=oFϿ>9xm+hnQuuz##[w.qXVuvCzrҸuC yZ}|msS_ ڒc9bPԝX2'5mɴoO1_>Pӂya\=;PH@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@R#d 5H@fC_IENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals/frontends.rst0000644000076500000240000007577614716225054020233 0ustar00twstaff.. include:: ../global.rst.inc .. highlight:: none .. _json_output: All about JSON: How to develop frontends ======================================== Borg does not have a public API on the Python level. That does not keep you from writing :code:`import borg`, but does mean that there are no release-to-release guarantees on what you might find in that package, not even for point releases (1.1.x), and there is no documentation beyond the code and the internals documents. Borg does on the other hand provide an API on a command-line level. In other words, a frontend should (for example) create a backup archive just invoke :ref:`borg_create`, give commandline parameters/options as needed and parse JSON output from borg. Important: JSON output is expected to be UTF-8, but currently borg depends on the locale being configured for that (must be a UTF-8 locale and *not* "C" or "ascii"), so that Python will choose to encode to UTF-8. The same applies to any inputs read by borg, they are expected to be UTF-8 encoded also. On POSIX systems, you can usually set environment vars to choose a UTF-8 locale: :: export LANG=en_US.UTF-8 export LC_CTYPE=en_US.UTF-8 Another way to get Python's stdin/stdout/stderr streams to use UTF-8 encoding (without having a UTF-8 locale / LANG / LC_CTYPE) is: :: export PYTHONIOENCODING=utf-8 See :issue:`2273` for more details. Dealing with non-unicode byte sequences and JSON limitations ------------------------------------------------------------ Paths on POSIX systems can have arbitrary bytes in them (except 0x00 which is used as string terminator in C). Nowadays, UTF-8 encoded paths (which decode to valid unicode) are the usual thing, but a lot of systems still have paths from the past, when other, non-unicode codings were used. Especially old Samba shares often have wild mixtures of misc. encodings, sometimes even very broken stuff. borg deals with such non-unicode paths ("with funny/broken characters") by decoding such byte sequences using UTF-8 coding and "surrogateescape" error handling mode, which maps invalid bytes to special unicode code points (surrogate escapes). When encoding such a unicode string back to a byte sequence, the original byte sequence will be reproduced exactly. JSON should only contain valid unicode text without any surrogate escapes, so we can't just directly have a surrogate-escaped path in JSON ("path" is only one example, this also affects other text-like content). Borg deals with this situation like this (since borg 2.0): For a valid unicode path (no surrogate escapes), the JSON will only have "path": path. For a non-unicode path (with surrogate escapes), the JSON will have 2 entries: - "path": path_approximation (pure valid unicode, all invalid bytes will show up as "?") - "path_b64": path_bytes_base64_encoded (if you decode the base64, you get the original path byte string) JSON users need to pick whatever suits their needs best. The suggested procedure (shown for "path") is: - check if there is a "path_b64" key. - if it is there, you will know that the original bytes path did not cleanly UTF-8-decode into unicode (has some invalid bytes) and that the string given by the "path" key is only an approximation, but not the precise path. if you need precision, you must base64-decode the value of "path_b64" and deal with the arbitrary byte string you'll get. if an approximation is fine, use the value of the "path" key. - if it is not there, the value of the "path" key is all you need (the original bytes path is its UTF-8 encoding). Logging ------- Especially for graphical frontends it is important to be able to convey and reformat progress information in meaningful ways. The ``--log-json`` option turns the stderr stream of Borg into a stream of JSON lines, where each line is a JSON object. The *type* key of the object determines its other contents. .. warning:: JSON logging requires successful argument parsing. Even with ``--log-json`` specified, a parsing error will be printed in plain text, because logging set-up happens after all arguments are parsed. The following types are in use. Progress information is governed by the usual rules for progress information, it is not produced unless ``--progress`` is specified. archive_progress Output during operations creating archives (:ref:`borg_create` and :ref:`borg_recreate`). The following keys exist, each represents the current progress. original_size Original size of data processed so far (before compression and deduplication, may be empty/absent) compressed_size Compressed size (may be empty/absent) deduplicated_size Deduplicated size (may be empty/absent) nfiles Number of (regular) files processed so far (may be empty/absent) path Current path (may be empty/absent) time Unix timestamp (float) finished boolean indicating whether the operation has finished, only the last object for an *operation* can have this property set to *true*. progress_message A message-based progress information with no concrete progress information, just a message saying what is currently being worked on. operation unique, opaque integer ID of the operation :ref:`msgid ` Message ID of the operation (may be *null*) finished boolean indicating whether the operation has finished, only the last object for an *operation* can have this property set to *true*. message current progress message (may be empty/absent) time Unix timestamp (float) progress_percent Absolute progress information with defined end/total and current value. operation unique, opaque integer ID of the operation :ref:`msgid ` Message ID of the operation (may be *null*) finished boolean indicating whether the operation has finished, only the last object for an *operation* can have this property set to *true*. message A formatted progress message, this will include the percentage and perhaps other information (absent for finished == true) current Current value (always less-or-equal to *total*, absent for finished == true) info Array that describes the current item, may be *null*, contents depend on *msgid* (absent for finished == true) total Total value (absent for finished == true) time Unix timestamp (float) file_status This is only output by :ref:`borg_create` and :ref:`borg_recreate` if ``--list`` is specified. The usual rules for the file listing applies, including the ``--filter`` option. status Single-character status as for regular list output path Path of the file system object log_message Any regular log output invokes this type. Regular log options and filtering applies to these as well. time Unix timestamp (float) levelname Upper-case log level name (also called severity). Defined levels are: DEBUG, INFO, WARNING, ERROR, CRITICAL name Name of the emitting entity message Formatted log message :ref:`msgid ` Message ID, may be *null* or absent See Prompts_ for the types used by prompts. .. rubric:: Examples (reformatted, each object would be on exactly one line) .. highlight:: json :ref:`borg_extract` progress:: {"message": "100.0% Extracting: src/borgbackup.egg-info/entry_points.txt", "current": 13000228, "total": 13004993, "info": ["src/borgbackup.egg-info/entry_points.txt"], "operation": 1, "msgid": "extract", "type": "progress_percent", "finished": false} {"message": "100.0% Extracting: src/borgbackup.egg-info/SOURCES.txt", "current": 13004993, "total": 13004993, "info": ["src/borgbackup.egg-info/SOURCES.txt"], "operation": 1, "msgid": "extract", "type": "progress_percent", "finished": false} {"operation": 1, "msgid": "extract", "type": "progress_percent", "finished": true} :ref:`borg_create` file listing with progress:: {"original_size": 0, "compressed_size": 0, "deduplicated_size": 0, "nfiles": 0, "type": "archive_progress", "path": "src"} {"type": "file_status", "status": "U", "path": "src/borgbackup.egg-info/entry_points.txt"} {"type": "file_status", "status": "U", "path": "src/borgbackup.egg-info/SOURCES.txt"} {"type": "file_status", "status": "d", "path": "src/borgbackup.egg-info"} {"type": "file_status", "status": "d", "path": "src"} {"original_size": 13176040, "compressed_size": 11386863, "deduplicated_size": 503, "nfiles": 277, "type": "archive_progress", "path": ""} Internal transaction progress:: {"message": "Saving files cache", "operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": false} {"message": "Saving cache config", "operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": false} {"message": "Saving chunks cache", "operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": false} {"operation": 2, "msgid": "cache.commit", "type": "progress_message", "finished": true} A debug log message:: {"message": "35 self tests completed in 0.08 seconds", "type": "log_message", "created": 1488278449.5575905, "levelname": "DEBUG", "name": "borg.archiver"} Prompts ------- Prompts assume a JSON form as well when the ``--log-json`` option is specified. Responses are still read verbatim from *stdin*, while prompts are JSON messages printed to *stderr*, just like log messages. Prompts use the *question_prompt* and *question_prompt_retry* types for the prompt itself, and *question_invalid_answer*, *question_accepted_default*, *question_accepted_true*, *question_accepted_false* and *question_env_answer* types for information about prompt processing. The *message* property contains the same string displayed regularly in the same situation, while the *msgid* property may contain a msgid_, typically the name of the environment variable that can be used to override the prompt. It is the same for all JSON messages pertaining to the same prompt. .. rubric:: Examples (reformatted, each object would be on exactly one line) .. highlight:: none Providing an invalid answer:: {"type": "question_prompt", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "... Type 'YES' if you understand this and want to continue: "} incorrect answer # input on stdin {"type": "question_invalid_answer", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "is_prompt": false, "message": "Invalid answer, aborting."} Providing a false (negative) answer:: {"type": "question_prompt", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "... Type 'YES' if you understand this and want to continue: "} NO # input on stdin {"type": "question_accepted_false", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "Aborting.", "is_prompt": false} Providing a true (affirmative) answer:: {"type": "question_prompt", "msgid": "BORG_CHECK_I_KNOW_WHAT_I_AM_DOING", "message": "... Type 'YES' if you understand this and want to continue: "} YES # input on stdin # no further output, just like the prompt without --log-json Passphrase prompts ------------------ Passphrase prompts should be handled differently. Use the environment variables *BORG_PASSPHRASE* and *BORG_NEW_PASSPHRASE* (see :ref:`env_vars` for reference) to pass passphrases to Borg, don't use the interactive passphrase prompts. When setting a new passphrase (:ref:`borg_repo-create`, :ref:`borg_key_change-passphrase`) normally Borg prompts whether it should display the passphrase. This can be suppressed by setting the environment variable *BORG_DISPLAY_PASSPHRASE* to *no*. When "confronted" with an unknown repository, where the application does not know whether the repository is encrypted, the following algorithm can be followed to detect encryption: 1. Set *BORG_PASSPHRASE* to gibberish (for example a freshly generated UUID4, which cannot possibly be the passphrase) 2. Invoke ``borg list repository ...`` 3. If this fails, due the repository being encrypted and the passphrase obviously being wrong, you'll get an error with the *PassphraseWrong* msgid. The repository is encrypted, for further access the application will need the passphrase. 4. If this does not fail, then the repository is not encrypted. Standard output --------------- *stdout* is different and more command-dependent than logging. Commands like :ref:`borg_info`, :ref:`borg_create` and :ref:`borg_list` implement a ``--json`` option which turns their regular output into a single JSON object. Some commands, like :ref:`borg_list` and :ref:`borg_diff`, can produce *a lot* of JSON. Since many JSON implementations don't support a streaming mode of operation, which is pretty much required to deal with this amount of JSON, these commands implement a ``--json-lines`` option which generates output in the `JSON lines `_ format, which is simply a number of JSON objects separated by new lines. Dates are formatted according to ISO 8601 in local time. No explicit time zone is specified *at this time* (subject to change). The equivalent strftime format string is '%Y-%m-%dT%H:%M:%S.%f', e.g. ``2017-08-07T12:27:20.123456``. The root object of '--json' output will contain at least a *repository* key with an object containing: id The ID of the repository, normally 64 hex characters location Canonicalized repository path, thus this may be different from what is specified on the command line last_modified Date when the repository was last modified by the Borg client The *encryption* key, if present, contains: mode Textual encryption mode name (same as :ref:`borg_repo-create` ``--encryption`` names) keyfile Path to the local key file used for access. Depending on *mode* this key may be absent. The *cache* key, if present, contains: path Path to the local repository cache stats Object containing cache stats: total_chunks Number of chunks total_unique_chunks Number of unique chunks total_size Total uncompressed size of all chunks multiplied with their reference counts unique_size Uncompressed size of all chunks .. highlight: json Example *borg info* output:: { "cache": { "path": "/home/user/.cache/borg/0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "stats": { "total_chunks": 511533, "total_size": 22635749792, "total_unique_chunks": 54892, "unique_size": 2449675468 } }, "encryption": { "mode": "repokey" }, "repository": { "id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "last_modified": "2017-08-07T12:27:20.789123", "location": "/home/user/testrepo" }, "security_dir": "/home/user/.config/borg/security/0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "archives": [] } Archive formats +++++++++++++++ :ref:`borg_info` uses an extended format for archives, which is more expensive to retrieve, while :ref:`borg_list` uses a simpler format that is faster to retrieve. Either return archives in an array under the *archives* key, while :ref:`borg_create` returns a single archive object under the *archive* key. Both formats contain a *name* key with the archive name, the *id* key with the hexadecimal archive ID, and the *start* key with the start timestamp. *borg info* and *borg create* further have: end End timestamp duration Duration in seconds between start and end in seconds (float) stats Archive statistics (freshly calculated, this is what makes "info" more expensive) original_size Size of files and metadata before compression compressed_size Size after compression deduplicated_size Deduplicated size (against the current repository, not when the archive was created) nfiles Number of regular files in the archive command_line Array of strings of the command line that created the archive The note about paths from above applies here as well. chunker_params The chunker parameters the archive has been created with. :ref:`borg_info` further has: hostname Hostname of the creating host username Name of the creating user comment Archive comment, if any Some keys/values are more expensive to compute than others (e.g. because it requires opening the archive, not just the manifest). To optimize for speed, `borg list repo` does not determine these values except when they are requested. The `--format` option is used for that (for normal mode as well as for `--json` mode), so, to have the comment included in the json output, you will need: :: borg list repo --format "{name}{comment}" --json` Example of a simple archive listing (``borg list --last 1 --json``):: { "archives": [ { "id": "80cd07219ad725b3c5f665c1dcf119435c4dee1647a560ecac30f8d40221a46a", "name": "host-system-backup-2017-02-27", "start": "2017-08-07T12:27:20.789123" } ], "encryption": { "mode": "repokey" }, "repository": { "id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "last_modified": "2017-08-07T12:27:20.789123", "location": "/home/user/repository" } } The same archive with more information (``borg info --last 1 --json``):: { "archives": [ { "chunker_params": [ "buzhash", 13, 23, 16, 4095 ], "command_line": [ "/home/user/.local/bin/borg", "create", "/home/user/repository", "..." ], "comment": "", "duration": 5.641542, "end": "2017-02-27T12:27:20.789123", "hostname": "host", "id": "80cd07219ad725b3c5f665c1dcf119435c4dee1647a560ecac30f8d40221a46a", "name": "host-system-backup-2017-02-27", "start": "2017-02-27T12:27:20.789123", "stats": { "compressed_size": 1880961894, "deduplicated_size": 2791, "nfiles": 53669, "original_size": 2400471280 }, "username": "user" } ], "cache": { "path": "/home/user/.cache/borg/0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "stats": { "total_chunks": 511533, "total_size": 22635749792, "total_unique_chunks": 54892, "unique_size": 2449675468 } }, "encryption": { "mode": "repokey" }, "repository": { "id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23", "last_modified": "2017-08-07T12:27:20.789123", "location": "/home/user/repository" } } File listings +++++++++++++ Each archive item (file, directory, ...) is described by one object in the :ref:`borg_list` output. Refer to the *borg list* documentation for the available keys and their meaning. Example (excerpt) of ``borg list --json-lines``:: {"type": "d", "mode": "drwxr-xr-x", "user": "user", "group": "user", "uid": 1000, "gid": 1000, "path": "linux", "healthy": true, "target": "", "flags": null, "mtime": "2017-02-27T12:27:20.023407", "size": 0} {"type": "d", "mode": "drwxr-xr-x", "user": "user", "group": "user", "uid": 1000, "gid": 1000, "path": "linux/baz", "healthy": true, "target": "", "flags": null, "mtime": "2017-02-27T12:27:20.585407", "size": 0} Archive Differencing ++++++++++++++++++++ Each archive difference item (file contents, user/group/mode) output by :ref:`borg_diff` is represented by an *ItemDiff* object. The properties of an *ItemDiff* object are: path: The filename/path of the *Item* (file, directory, symlink). changes: A list of *Change* objects describing the changes made to the item in the two archives. For example, there will be two changes if the contents of a file are changed, and its ownership are changed. The *Change* object can contain a number of properties depending on the type of change that occurred. If a 'property' is not required for the type of change, it is not output. The possible properties of a *Change* object are: type: The **type** property is always present. It identifies the type of change and will be one of these values: - *modified* - file contents changed. - *added* - the file was added. - *removed* - the file was removed. - *added directory* - the directory was added. - *removed directory* - the directory was removed. - *added link* - the symlink was added. - *removed link* - the symlink was removed. - *changed link* - the symlink target was changed. - *mode* - the file/directory/link mode was changed. Note - this could indicate a change from a file/directory/link type to a different type (file/directory/link), such as -- a file is deleted and replaced with a directory of the same name. - *owner* - user and/or group ownership changed. size: If **type** == '*added*' or '*removed*', then **size** provides the size of the added or removed file. added: If **type** == '*modified*' and chunk ids can be compared, then **added** and **removed** indicate the amount of data 'added' and 'removed'. If chunk ids can not be compared, then **added** and **removed** properties are not provided and the only information available is that the file contents were modified. removed: See **added** property. old_mode: If **type** == '*mode*', then **old_mode** and **new_mode** provide the mode and permissions changes. new_mode: See **old_mode** property. old_user: If **type** == '*owner*', then **old_user**, **new_user**, **old_group** and **new_group** provide the user and group ownership changes. old_group: See **old_user** property. new_user: See **old_user** property. new_group: See **old_user** property. Example (excerpt) of ``borg diff --json-lines``:: {"path": "file1", "changes": [{"path": "file1", "changes": [{"type": "modified", "added": 17, "removed": 5}, {"type": "mode", "old_mode": "-rw-r--r--", "new_mode": "-rwxr-xr-x"}]}]} {"path": "file2", "changes": [{"type": "modified", "added": 135, "removed": 252}]} {"path": "file4", "changes": [{"type": "added", "size": 0}]} {"path": "file3", "changes": [{"type": "removed", "size": 0}]} .. _msgid: Message IDs ----------- Message IDs are strings that essentially give a log message or operation a name, without actually using the full text, since texts change more frequently. Message IDs are unambiguous and reduce the need to parse log messages. Assigned message IDs and related error RCs (exit codes) are: .. See scripts/errorlist.py; this is slightly edited. Errors Error rc: 2 traceback: no Error: {} ErrorWithTraceback rc: 2 traceback: yes Error: {} Buffer.MemoryLimitExceeded rc: 2 traceback: no Requested buffer size {} is above the limit of {}. EfficientCollectionQueue.SizeUnderflow rc: 2 traceback: no Could not pop_front first {} elements, collection only has {} elements.. RTError rc: 2 traceback: no Runtime Error: {} CancelledByUser rc: 3 traceback: no Cancelled by user. CommandError rc: 4 traceback: no Command Error: {} PlaceholderError rc: 5 traceback: no Formatting Error: "{}".format({}): {}({}) InvalidPlaceholder rc: 6 traceback: no Invalid placeholder "{}" in string: {} Repository.AlreadyExists rc: 10 traceback: no A repository already exists at {}. Repository.CheckNeeded rc: 12 traceback: yes Inconsistency detected. Please run "borg check {}". Repository.DoesNotExist rc: 13 traceback: no Repository {} does not exist. Repository.InsufficientFreeSpaceError rc: 14 traceback: no Insufficient free space to complete transaction (required: {}, available: {}). Repository.InvalidRepository rc: 15 traceback: no {} is not a valid repository. Check repo config. Repository.InvalidRepositoryConfig rc: 16 traceback: no {} does not have a valid configuration. Check repo config [{}]. Repository.ObjectNotFound rc: 17 traceback: yes Object with key {} not found in repository {}. Repository.ParentPathDoesNotExist rc: 18 traceback: no The parent path of the repo directory [{}] does not exist. Repository.PathAlreadyExists rc: 19 traceback: no There is already something at {}. Repository.StorageQuotaExceeded rc: 20 traceback: no The storage quota ({}) has been exceeded ({}). Try deleting some archives. Repository.PathPermissionDenied rc: 21 traceback: no Permission denied to {}. MandatoryFeatureUnsupported rc: 25 traceback: no Unsupported repository feature(s) {}. A newer version of borg is required to access this repository. NoManifestError rc: 26 traceback: no Repository has no manifest. UnsupportedManifestError rc: 27 traceback: no Unsupported manifest envelope. A newer version is required to access this repository. Archive.AlreadyExists rc: 30 traceback: no Archive {} already exists Archive.DoesNotExist rc: 31 traceback: no Archive {} does not exist Archive.IncompatibleFilesystemEncodingError rc: 32 traceback: no Failed to encode filename "{}" into file system encoding "{}". Consider configuring the LANG environment variable. KeyfileInvalidError rc: 40 traceback: no Invalid key data for repository {} found in {}. KeyfileMismatchError rc: 41 traceback: no Mismatch between repository {} and key file {}. KeyfileNotFoundError rc: 42 traceback: no No key file for repository {} found in {}. NotABorgKeyFile rc: 43 traceback: no This file is not a borg key backup, aborting. RepoKeyNotFoundError rc: 44 traceback: no No key entry found in the config of repository {}. RepoIdMismatch rc: 45 traceback: no This key backup seems to be for a different backup repository, aborting. UnencryptedRepo rc: 46 traceback: no Key management not available for unencrypted repositories. UnknownKeyType rc: 47 traceback: no Key type {0} is unknown. UnsupportedPayloadError rc: 48 traceback: no Unsupported payload type {}. A newer version is required to access this repository. UnsupportedKeyFormatError rc: 49 traceback:no Your borg key is stored in an unsupported format. Try using a newer version of borg. NoPassphraseFailure rc: 50 traceback: no can not acquire a passphrase: {} PasscommandFailure rc: 51 traceback: no passcommand supplied in BORG_PASSCOMMAND failed: {} PassphraseWrong rc: 52 traceback: no passphrase supplied in BORG_PASSPHRASE, by BORG_PASSCOMMAND or via BORG_PASSPHRASE_FD is incorrect. PasswordRetriesExceeded rc: 53 traceback: no exceeded the maximum password retries Cache.CacheInitAbortedError rc: 60 traceback: no Cache initialization aborted Cache.EncryptionMethodMismatch rc: 61 traceback: no Repository encryption method changed since last access, refusing to continue Cache.RepositoryAccessAborted rc: 62 traceback: no Repository access aborted Cache.RepositoryIDNotUnique rc: 63 traceback: no Cache is newer than repository - do you have multiple, independently updated repos with same ID? Cache.RepositoryReplay rc: 64 traceback: no Cache, or information obtained from the security directory is newer than repository - this is either an attack or unsafe (multiple repos with same ID) LockError rc: 70 traceback: no Failed to acquire the lock {}. LockErrorT rc: 71 traceback: yes Failed to acquire the lock {}. LockFailed rc: 72 traceback: yes Failed to create/acquire the lock {} ({}). LockTimeout rc: 73 traceback: no Failed to create/acquire the lock {} (timeout). NotLocked rc: 74 traceback: yes Failed to release the lock {} (was not locked). NotMyLock rc: 75 traceback: yes Failed to release the lock {} (was/is locked, but not by me). ConnectionClosed rc: 80 traceback: no Connection closed by remote host ConnectionClosedWithHint rc: 81 traceback: no Connection closed by remote host. {} InvalidRPCMethod rc: 82 traceback: no RPC method {} is not valid PathNotAllowed rc: 83 traceback: no Repository path not allowed: {} RemoteRepository.RPCServerOutdated rc: 84 traceback: no Borg server is too old for {}. Required version {} UnexpectedRPCDataFormatFromClient rc: 85 traceback: no Borg {}: Got unexpected RPC data format from client. UnexpectedRPCDataFormatFromServer rc: 86 traceback: no Got unexpected RPC data format from server: {} ConnectionBrokenWithHint rc: 87 traceback: no Connection to remote host is broken. {} IntegrityError rc: 90 traceback: yes Data integrity error: {} FileIntegrityError rc: 91 traceback: yes File failed integrity check: {} DecompressionError rc: 92 traceback: yes Decompression error: {} Warnings BorgWarning rc: 1 Warning: {} BackupWarning rc: 1 {}: {} FileChangedWarning rc: 100 {}: file changed while we backed it up IncludePatternNeverMatchedWarning rc: 101 Include pattern '{}' never matched. BackupError rc: 102 {}: backup error BackupRaceConditionError rc: 103 {}: file type or inode changed while we backed it up (race condition, skipped file) BackupOSError rc: 104 {}: {} BackupPermissionError rc: 105 {}: {} BackupIOError rc: 106 {}: {} BackupFileNotFoundError rc: 107 {}: {} Operations - cache.begin_transaction - cache.download_chunks, appears with ``borg create --no-cache-sync`` - cache.commit - cache.sync *info* is one string element, the name of the archive currently synced. - repository.compact_segments - repository.replay_segments - repository.check - check.verify_data - check.rebuild_manifest - check.rebuild_refcounts - extract *info* is one string element, the name of the path currently extracted. - extract.permissions - archive.delete - archive.calc_stats - prune - upgrade.convert_segments Prompts BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK For "Warning: Attempting to access a previously unknown unencrypted repository" BORG_RELOCATED_REPO_ACCESS_IS_OK For "Warning: The repository at location ... was previously located at ..." BORG_CHECK_I_KNOW_WHAT_I_AM_DOING For "This is a potentially dangerous function..." (check --repair) BORG_DELETE_I_KNOW_WHAT_I_AM_DOING For "You requested to DELETE the repository completely *including* all archives it contains:" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals/object-graph.odg0000644000076500000240000006741614716225054020527 0ustar00twstaffPKm!Y.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKm!YConfigurations2/accelerator/PKm!YConfigurations2/toolpanel/PKm!YConfigurations2/images/Bitmaps/PKm!YConfigurations2/progressbar/PKm!YConfigurations2/popupmenu/PKm!YConfigurations2/menubar/PKm!YConfigurations2/statusbar/PKm!YConfigurations2/toolbar/PKm!YConfigurations2/floater/PKm!Y settings.xmlZms8~"fzsCi؀Z&(ȒGC'Rb\>,KϮVճ+|} pȕ#.0/r?}M؅(*T]đNEu1vIV ]3NJa[A*C*Etz蓠K_$XU0@ͻ,ʄkPc}^y2:(ޯ?L6\,NjrlRR)o6醱WO*^XW.R՞ j_sY}䱄@ѺY )ޓK1z,hK KLe|V>Rx [mDR\ =9K?=?-U2_g*g{PԃG6e"y1ura6+_մgY^K?aY覍!RKeD!1mLa;W:\5cjLDQ- v]?P[l_$8I1V RXL;L,z=hM|$ө\rqqNFO>LKi3w" Cu%U0xaoUHcunnl3ƕng-`2r`w>GC3X@}}b3 g58M.U )Of7SNj_:RgCwc۪x9g4-VRW$ㇷ̋ʰcґ,&rW_aJX00>Ճŧxsk*gD Ib ^k"_DŽw axg,\}$'enPGWxR;C6oj^뀴*D[-5:XLPNS,K!cj:f1 =;CP/~zue30( 0%Iw6R~S!*A 8RŸeHk- $ޱ`BxzFTJ}ƍ(f ]G`32s䪳TfB K 6cse(ȯ2v3 gQ0Ê{;v5A5ٝd| bk ҅0)o Kyg:7N3,nzu&'}ҽǓ@F5,z4'qs<-hl5wPX}?_/z&geg+[7+l%ePKs)PKm!Y styles.xml\n:ߧ|{Xb{$=M^mE/~$H[vlhbrșf8$NLW=98 I˫ޗI뿼!EYDO 8g(W Mgq>K" ɬEc6Dauz'&8gݙm̾<WUQd~^l.?-cWKEIuy1Zݬv rčqX駢aC(Bui)fFkK\6!%dcceA6i$J,!у٥ekTK%LjS 6,-_!{85=aY3iV4;SZAQgb&'}䆶ݹ- =6& .ЂnCUvWjp M8,Q Vy߿u Q1>>Q5 %M{}16ʱ1 -e c R3s3ŧ.C&6޵ b7a_^x]%Η4f6-zCK(J{94p8e¼  HٜXo0Nҽ_?7Js疤V] PDhGU!4}?MM|qjޡ)%kT)WWHME'ـ%H ҌxQq>tR+A"2%ΗjR/"gbVEW<{.r׺~i(f1ោ$a!DMuQ]ilI[l? "?VX.y\0}}Ѩ px^ʢ9,UJm$8L -2]lpI_~%` Vxc}Hգ8$Qn`l5䶒@g_)iLֺQr?&'8k}AVhUi1PqleQtWG {͇oCًkrr{I  e8m@qX0@66Dxi'm$k`Zq+K\ W 4u(a^-߫S{.+Zg?l?<+Q+Y[YOZOzNﬣ/h~Y_F?h?8'wZY^k;kk zϊyyA=v#L޳=)WDprՀםG7N޽ /FU`S51[pp$[t!RR;eJRLmh[(**ӲlMⰧhg7$ôq΍(Ux E-l˺rI&ąϥP3X*8uѦ y@8D$[rIbOHxW<^gp,a)F1)k2ԉg8ӥ gPlͶ)s+'Ap &@I5Kz59h&q$[38Ba`YwLYǢ%++&-(^.cZ%tD R5>C8McAg *YUTWW4FDFǕ3X?$|#U8bzG,b+r `4͢1\Q~m@ꡩϨh 5ڱ1Bd^un" F Q1'EŪ^+ƊqUryu 6c1wV?Ԁcf{ZI 7ߤ8i/ ׇdDhxB& .J|.O+ӓ!4|qzzB^gCA4Q-^cڃfۦ%(,rw! ն.[Ljd1OOh/vQ# mŔ߿5J^ \II;!]=MR]Qŵ)Vl+WLD٤12#w]fgmqEߗ Ӿ~s 33Y*0VOW\)k դ/vHHaOUo%I1"km ԓuyOMve-MRǡ;^g+$îy>/U&6 "2Ds^'ZQY^s WTGFq+#VS'1uK`D|ۊz8|24j8y$#V$ܓ!5.xt;[1Ra  ?;`3'qnJ3 4ꇜ wVvEMI& yu8Zp1.:lS#< 6nJPR,'[WAS}x-/zA}ֽOsJӡYb4-g4f_p4C212TmF `@dk%ovg øNVQ^T~ S pa~6_ەR.4 .' ٶbQT zPe~9Jg-]xaU׋^c9ȉI|aE "onѱM;Ĝ̢XH_,km>#kgB'?Aop_x:46Dqpšu8mNa3xZ5\=eʐj Gnm&s>.Qel Ax PKpd @YPKm!Ymeta.xmlTKo0Wh`-(T(UznF؛M}ynHVcfp9^o7z\jLw94|,'UN@rXJdcO)jmR+V1XR< x0a94^P8x _7is,q5Ո砝v8 y /]O@ڴvB_~ W@|N![f@^XfG窨!Hznn3=Ve^AYYBV(L*K%}E{K3NU[VȇQEuK7Ba%8@[nDt+8C x\'=@'V_U/|M{JG(Сeu"e}UOhpnhڕ"7SDvQd 7K@z-ߍz ]riZ@% {a`9g0 ^:\5*OTWKHWR H ȋwg+ ok޷ߐ`*^z%t!I`^NOvlHm9ת"1כb1w3*P7X뚂^qo`6=+s^yQw\Upr>K7'L<<.V8g`Ʈ ^9qaU>gAf(zNjI\׳ nƅ ,M1fAD oX0ʤ23bUlTȥ'*EEV΋d? &#e6}Ŗ ˮL[GU蔅i8p*2r) ]ɘ3v1L{-Rt(Ad,Ƅ^H?r5f 23Bp8{ۯEZдǰr.pp<; V-"@^~e,>Yh[ Bm!e$OnK,W줲!ė[+ .߫wpI>8@`E `m s_˨8We 3B2*I*($DīG6Ƿ 6!183yRobUd*@9{tgA6$ ~ye ;Z?ǿ<> 1/ZH~E-V^$񠑅\_UX cV㹐mf2`F&o滖ńߴ9o,rR( ` *F woP([P0KCW.j8_b5 8 y"/9@>b#6UGz @c9 8x@f]uՎ#YW0hx55$Î}6J֩F%6ߵڵ8?e!EnUxlP^+ ! cʠҠ1) _UFmGBЁ&0z,:5轂Bȫ 0;r*Ru{YkrfiYMuBF_~PY5m7MURIs+Z?Zq@]T:2( =aohpЭ Tѻ,ɀnVzm@߰ xaUzyQ^.J}?r7"/~,mٖ+r\  Xo2"~nX/Cxp OAjXڶ Z׿9 oMh$ 5t6#'8扶O(>^WM|ޜ0e)9 4'-tMhp Ygv!B;e!% +4N$w?V@]31 4U]xp*Jr&W0{@ũcX{-A9i{״(!8%ۛrup >T~X> ĿT8̱I y=*d-mC 7N~~F T`I~vVHidxeP֏i.@:z@HO_ƘNR>Ӥ< 1!0(vN ;ˍ4x,'vf;7Z#Î|"#Z@l<pж܂EIlt=~6hPr4v=!v $Gq,fՏ-:!4\t$M7vY`-6r ݋e @pԧkq!p;w}ӗd+B92@H!e>*q@4rߕ?ȓ} dc!p0 r~] .2.0O؍ z> Gq3Ù@躏%w3:|LeX<SRJ_ݶOdDOlKHِh@I+/Բ.x/j0?cĵOdӿ9D[> h~2thC[֮-d/]BjG?Ϧ:vmbx~N?]X!I+h|RQ?ϧ)+h#,p 7':3u'殠%Ot駴l$ ˍ{ )ǜ~҈X>M窭,~fƏ6tZI+h-`KHx#N?|IB+h3[-mUD;d*.!mѤXǜje'殠6JG]>sD;넫9wm\B,k9w}@$R*~@Sh{W&Y+0=ɟB` ' 9.!GDa!I+hC`UАޟ#ܐ.I+hS3' C+7~*oQ?Q!I+h[bDjN\A`Kh͟%~?*u u|mݾs <@^UazDsCw)%×bN\A[(o|w=hhW%?:&+{6.!: 2,qYqZКDILS\-9Z~w7) ྜAp z~N=\AMQ]B=hhW2>;).ODD#k#ռ]By1cHON@|p -?"ƜDvhͧ!D#N]\A;՘Kxn{ԏ#b~A'Z*g~"hW.[KN#'"v%~"I+h'T~ERODq O?mQDD!ݚ*]o~؈(;8'Qxha{+ey 6;w0~s0EJ,G2į$ O!ID @CS+Vrv$dO% N"dUp'!8羙Ũ'>}~wApU=t<- We,w#kFV\?sC>gph&}u¹T=o2̱]L-s[Ts2{&? [ 7J@5x&KH_ӿH;,;\[|pyŶcGA8C@ #/N 78N ps? 0LIUd?J( zU &Ll+A V1"‚|_f%,pt -  pLSvAnr"pd 3`HY.< 'g1 ï N)f x2ƦAs@`4M77 ܣ&w6;i%Bgį,d"P>}CڙTp$o״<#Kevi- myi9zAV9enCެ\4G؂Dx/?²l]ib} CȪ#yv) <˅i 2,GHz[aȎAb9@ .c }ifŰoVgdi07cA,/mWpf'@Pѐ^ }R\0T2| SµbIlg@fYư?"(Q;0 /(- gϖA~` ~@`42 S*GK i[F>)xhe sP8;C vRymv==~ 0nǖ1-`hFVgw0} v.=G̿2_}!~W+Dt+\"2+rp;ԀW|rѬ~YEýzHĕD06TկfUO\-/Zdg>PnIԕڭzN-7x}-!J{ 35B9ci oyڐrÍyĿ"'(i 7 TCŏXֺh&@BhW) 4 IDATze~ 1*zWڻ^mg4= h}R Mx?R/B[-DYN~ \jmĊ ;}n1,-hzJu%7#d- isCPs{؞]yMOh~hI pęJu-b?{uQ4Z 1m.Y,4F [ۣ >ĠŽ%Ԟ%OZ¦ǯ`)Zs_ˏH]<փ ֺT/lo]]@9@]t..@.!9h4PE詜 dm. kvntore|>\@ r\\EɅOZѐRdZM橽jO<  :6FU vSۇ]%q -qP3^J'@?@f. ."Bi01@k H@Wh "cl f[/ a@ݡCaq6GwJ1 HKQ`}Vt <sb4K9j,:R8)@"hxȄE&f r%]ɘGJe"Ŗ/ *" 9eh3.8tob m҂$ pG{S(8Z Lܔ($qB?h84 E*4IH?Z/5\q˔KD8 HQu[TsLz"$ 2B3uYim$ѹJ[AdP5n `VA  4*hm7 )@W`(UL:pqJu)iH 4 2@Gf"㘌qe~Ֆid& l3RHq$ˬ (DNC"QRi$.9P BjfT$Y"$L,CZf̔^+QdA֊8[yxp2HJ=hB<DʷK A۳ZqY6Җrf@}$",35@286fJT)ej.<ۧ8@Q{PZ8@mg$m!٦$in0"S6`PHd$5ziqZ"s?Է΂'mևjtJjd[S{zd!C.Ö' UZWP+-~j}d0D`Ҋ͔!G"u$ W}|H`^ߍRx$ /*U_dh8U\}=էn B |ЀDH kL` ZRi== &{zgWWkjrtt4Ϟp,t$L0&3!Qf@0R`@iRg΋1G iCW;Z[@-33 JO,4},2Z|w%D;8ՎSkGn:bGfHagGaLMSn̤==OW@0Ri5sS%nqOD'z`i[s@;U }1Km(oGjeOuTQѣPdQxzZC xzШ}tN_ L j<=ʆ~/3 ͐qvQz~vާ6,WZ$ x[ jߛC  t {n U Id~YiS#Kg=e;1f̌+!z%Nõ-sxZ4<KSoPZ?xz>O  D9`@"ך1hn48̇'nHLUV1WI==c~yUD}+IRP+-Z1󡞧3  T^H:=f63LfjJ{O@ Ol <=h@w'"[]xi[p4Ji=o|tt8x`9@OV$*-oG<zǻ&O7hLL f%Y]eʊJH켒/?Ntٲ۱dC.49GU+'>yږ@9mVr +MH07A]2(R@L4#GTZ;5)gKORxIǠM?3*@lGS"k})45! hBRf{!) HZ>D0Ӄt*($m9RplOO&E5@rLO! Ͳd@=q{z$ojHL;S%b9, U֋ Џx% BAfaa |IV:E3[8?MbD?7%/3eL\;g #@B )`2kSrEO>Pwp2EZZlxG CSML, g,2*4  7LOԚ=G9?3 ikXz"m tĔ~@K JE$\^i<=d iOtp()mǂ|[O9+V@x0mmA 1~yuy94eApB=859A`aEґ,p#"Lr&Ov2kCnkOp`ȶ|bi:ZCCJ<6V< G5) DPBQe "cL:"P/2 @;chGyzrIO΅qgI8v &5r%yJK?%=)w:gQА #$h}YgzOyK~s#6,B%g櫉'0IӂfSYDKh/3n^Ҝdx"]}<)\^fd&ϖB I=ZX:==%Gh8bjQDVz o祤hg7V02\/i&`cbsމ~314uBs?dğGG$)Si/p{vgpan(9A_iAvvi .j)n .j0ii-4SHG"!ម9)`'=RbqǻR|O Nu(8'Ÿ'B֊or]>OPOTwpf>nZ_{JQFwa SjgqfI%}%-<q&wq l,*n#tơL9}?4i5@CAcy&Nǟ6Kptv߶(<,,S%iF- ppwd|.q# 2M+C'D$2 !'*hvhsbH (ms6@6um6o@d:ybIvm6 gYocahE|.)M ]0KE0lw|&3&4ĵ ϵa\HGMH CE9q|1YQVBm #;ZB0j E&¸m6!Rd%bL-8|+v w"6&ӈ%Dj,2.9s!8Y&\PeNM8<};.u`t\}Pv`rv^6rZx;;6[DH8o92f:8q~c\eYgROC?B: J eO &.wQx0r0^F.bq@zR!w %G?3[Qjyd$ Cj%ypLɔ/wAi H8nx.A' 9SsJJБd'%@hA*l:p1=5q\.HwFn ĠROqGOL=rZ^i#hfa /5M0O Yw3:ԗB"v)}uO!BPtQӚK5q].e"grk3p悎~6w f'. ^Fi@MwexRPc= ^Z j@IMHeRL^f"!d&:<Nf%x--J AAhWeӎHOKDي6Ngpc# TH3`maF\4Od r8 aZ:[e=~" N Of B:]5q=^rZkNMkE?YGi˝UKD0dMVB{zj:G <Lm 욷B'3MhRu  _KsxCyP4D?{,_䈓gSS$$bAi)قy@ᒧY/yO}&yzSYON/?V2M1ǐ2QLK5+x>k(jպ͏ɻ*h`bzÜkT+HP%OR1 )n3"}wM"U* IU:EPW@SH,'C9: }b; A1UK^T_*?TxRHQoKBU&"P$ﲸTV鐳 p>^S'$O܏Z<Pr$WS`~ 1ҁm(Nĥȗ3i;PgH<E&W>r>>RD!n *Th̤̀.ӂ>X&g(y2w98@= :yZ1\f)c]M ua9tv^4^C%40 P Q UR0t $|m$8 G %OCDzeZ^uT.]C8I2i4I( @c琠41H*#3N"jJtHeZ겒4K$"(2- Z-29SBjWEN? j,l(ـTD 2d eZcZieK ^I1=mv^~9wR~p4!e) %jV {L`QmL,XIIi 5AӃiHW'IN>&-%R6DTO`~z*#2h& uJJ W& N&%Oΰ5\s ^H5vҮU:TsNH5 LzӐ:yZVZʉx]i Ֆ)iRegHxOeBI+[}.ΉxV10TZpEɆ9 kp +qϐO(5Pg(LYߨ,T(-~R6Ow!IgA=C?6\MJJ 26sդPQ7IQf,f.uϠWN"7R& əi[IpM|#$V@J RPhքZPOKWWj@iQ9;5c| 8vL9}HWPɬ.i"DOO`4iMeI2TfЏLx9@+H \Hȝ55#U+Cy_2JoGj~ $zzpHtLxeIRA;/qI-}فn=>j5qiM]KǸi\ܼ!R5%6[d2kȋ6'ܲDT]i y@;P7nů`6is[nO(5o \;51po |OJ&T%H5O[,\{k,y,i(71p 27qlǶm@;Wxg42 As&y/豇Khp{Wr膀!wPR~cvM& Z\tB=ͩ=W%}ut!/]D MD^j)2/~p#Zg g;pg;pg;pg;pg;pg;pg;pg;pg;pg;pg;pg;pg?Œ3hQŧIENDB`PKm!Y content.xml]ݎSJR$mEIԿEE;)DUǞ\ zٛ$%KdGxCG~v#NRDSQ(%ͧlސw#2Q&$@KGE~8I4!(IBN2wBbe&Y&<1 `hLƴ׫RA^euieSqeDWXi$sd'exɥЦ5Zхi C -+E7FlXϭIa\Je4=bLbF qwˬL5Q~пl:;WXcthlPi HXm\? 71>ykﵭ&ޔ|I*aF7 X7or5>N|?F>7aN/eni*$`* Yi#j>,ҧ4aC$3j[wgpT%n[C;$b*~CNC$R}毱O>]*%7ӄS֛Uϡ$#$Ϊz}?>;ͅo{5ɜE;<'X^Jh-s ZTm]ft]SͿgCW/V -]yUtxer)/DnYf3?IJ#N2ӄ Nh%|+6ŖyOP $17F lODN&k$s΅>LT06!=ҮPEYlg}*sk3se=x{ѭ aۭ?/̟= K1Ói"+QfdQ0EzAI5.vxyO<o 9Be.kp#/\r /hsĔ-`G9Gօ+x@σj-iw˫f yuVm(,5i;U96 $Ds6ü#Yx̲s&G8"8|~H6#>4t< <х͈Ck#>G'f )#>Ӏm`>pڏdX_,&iQ{U E gt:ϋc%(]VI kT;6{mBJI2Ŵ#+K#+[GӴt ˮ|i^3CH;B*~BD8By2:>:O'CHy9!B9ONiMԵb-n{7=dqZsyP[ *AS/ re.Ni`q"<}~eZe{ %|д ~h 4j޵~a0{|kP:ufc^3Htq\ϰ}pX&<Vuց`k I3ռb}Æ>:\*/nCO>y_H=j?VP7x3p{3(wxh}zGM36U35;!,djGSZtإF⹊VQXi]T=NhaծlMjS&4VC9{󧜡Yu-]Q+ EyWuLF?rKRmނZWsog=5JRĽwj cNDȦ+KORG~duy,^-;99k2ekIMM8q1cfΜY9 **jGTGb 77Zrp̘1wQ[õ^v_qgN2>ې_z$44t4itŋU]QQQlllTTTAAA;::~>|8ߟ?G5޾~N<9tb455ӭ[Oro߾-((h޼bmq-"z:WW}ݺus @ƓH$ʴAÆ vPYZZ٧O=66aÆ-ZR;R^^.III666wܩ=?{GyZݻ_v->>+9N81bĈ3fl۶ ?C0~ __ߒ9sH'Ν;N.Ӵ4___"ſ򋃃tٳ;fmmo>wwwvz,Wҥ [YYmjjjyyyu۾};'"Hinn.奨 "ϟ:uD" TVVfaK.f-,,.\X}UV qڵk>}ڸqŋA:u/H"l߾/_TRR2552d|!ˁ?H$Æ =z47PƍDddd4iҤ[n5lp29|}LMM`ٳ[lݷo;pozo޼ԪU+OO϶mV>͛Q= @= |yyy2/ݻw^N<ζs WKKKY{*,lDdddDD***, ''G؝ɓ'W3k֬QSSv&33S& ,,LYY PVVVSSSSSҒSQQQSSSRR"'VdvEMLL6m$ Y‚^_лwHQQu 4` ;v,MgQÆ r[(//|)3mڴ?Yf񙙙o߾= %iӦI$}pXLyy#bcc%KJJت\E˹@ Ǐ'"WW/uח]6h EEɓ'WdNNN~~ag"H$D4}t"D7 =rH2m4"bn߾MD5Z17o$prriȘ˗/wtСC˗/700LLLj72'p*O])**gI %x5:}6cJܼySYY6nRQQau4;FD wa]Q9%%%OwM}/,,ڵk6'>>?~LpIIIff>+ a5ʕ W^8b"1cǓ _PPPA=ykQCADmڴ!Ǐ ^p+ZI5`$VAS[[[}HoJ s XRdb_=e-=uu8a6B'SRRH$xuiIIIzz:_ iAA;*Ҙ'@݆ P_Ν;-ZԱcG=='Oь3K lEbP(dJXG`5`{FC\\K׮]ϝ;wڵ&M5,Y[[=ztڴi5jԨ[:֯_I^ ZZZIKK 23/Oݻ+((S$ѣYK.VVV󏡡![255ɓ8 0n޼9rN:q Hhffv Ae7fl{nl={tܙ[888dggWÿ 7G\{u;88lԩSD4nܸpaxқ:ttyxxѢE2#,tttmV9O:iӦܫjfii)2|p":uT鍨;V$I$͛7%ɪU($$D"58"==]z`Űa~W"ڵk:ez IK3f̑#Gݻrqq)--:O`ɒ%DzYfkkk6}>¢]v奥0HTTTZbvnnnLLL k޽b%%%MM<"cccccc"jԨwСrNN:u4vu-\pƍ)P`>}z"9sf͑eee]t:t}EE… 6lxQٳ 6twwZttt٣ҫWƍ߼y{ٳg?k?,X@UU8+?m j}nݺU;"RUU%" jڴ)g) 7(((O+ѰaC"*,,Y`D"P;JJJHCCg@}ZhA7[<#tuu'O,bqtErr2pzyjՊݻ;yòeee\KffGo… wﶵMIIŖdU^A~A^VVV;w& 6ȼ$ ;Cbb""Bl1gϪ8pɓ,Y" pV7/|k֬!˗\2##<::[[["ڽ{WsFƌA!.^x7oQ||ŋ###sss"""X'|_zn:"644TQQqrrb {633{w}X}H$*ѰaƍW^^ޯ_ߙ5k۷W^w!޽{a߿ uttN:RD||䉚mllVde3fL퍌wwd===?033xs8Æ dE"9{v^"4iƆM'sCa~  /@^/_y!B~>Xt@}|vvv/_FWO/'xΝ^z!P?!  !P( ͛E"J _β ֭ٳ': I$臏,@݆P.]Bo=/@IHH~A SVVFo=/RC|~dd۷o|֭eY?0~>=dSBf~B~>X@} :t z ]rB~ ֧Heee "eel߾_%%%?SnfΜIDs)((ػwtcUTT'88̙3sεpҤI&Mׯߜ9slmmΝse˖mݺvڥvZtCȯ+W?~^iӦ4duuu|}} &}>Ν;!!!)))7nRZZzĉG?Dt#G\r' I'OD'@e'N@'@+((Xf Dnݛ F+ 6lȐ!]v `)պukggg}}碢[֞ettt(33p@TPPN P?a~***m۶{.khݺu^^G:uԩSڙzY";vlRRRqq1-_ @%mQLL k H$cyy{{?x@:fݺuW^MJJӧo&jjjeH,O>SN2?7mtΜ9s΍sss΂ 󋊊իWbllldrN믿ݻ׹sgќW^Xm7o;w^jEDV"3gш#7os??@}` ˉH(بQ#wӱcAhhhhhǥ eeRXb򴴴]VQQ1gsL,QTT$_3f̑#G8!! \LVV־}}||Y]\\\\\(,,LWW_% .E?CBpDF"\R"H$E԰:;vw}' ^˜WTT͚5-kN(޽W_xQ\\CD666EEE'#G|255U(O^^^?ŋ<ʕ+D(رI&%Dհ H$FFF8a>c IDAT߁]ve3)ؽ}"ڱcmڴI[[ V^͵D5Ç\ɓ'Q#?CAAʝ!gvޝM #>HsO544nĞ>}X cccɳg6l>o D$N>fO(((4m4==Pfgei zVQq۶mBaÆ666JJJ%VOx==="244~ʶC8IIIA%0(;;[&`ii/d ())UjBڡKD/_fE+9D&Ar@ _AFFFbb"7ɓ'F|w 7&LssR P%w2ߟka9> R,8~x">}tP(t)x_e^:((둑D,^X$xk׮QPPЭ[E'OOѣǕ+WLLL ڱc_~}cǎٳݻ... Ϟ= ԩD!T9chhxiDD[ݻǫA){IѣGLr? 455322Fy…8n*yEE\c—lnnI~n FFFlJT,99{bkkn=A@= _Wu%߲eK|)P  /@^/_y!B~  /@^/_y!B~  /@^/_y!POpYYYzzwᄊx<r;WPP P _nݺխ[7???>*۪֭UR222|}}+ݻxbj (**~[|>˖-<O${JMM'ܹsxxY$WKǎSVV4ik)((رٳTƍ;v/3QD|SSS---7o.+)**jjjŕiF___:痔?YYYڵ 2QTT|arriVd>ݻw,`dd"ޒ2 qlȀH$jԨtP(|iVVV֭٠iJJJO>MMMmڴx<ۉVRRb{u__N:h"??ήtժU;vz2#u _ѣGgffr-|>۶m mΝ[h1mڴ9spׯwuue˽/U--#Glْk)++4hSkkǏsUUUsss.xĈ;w6lذo߾3f̛7k/,,Сx5 7n6ծ]H' W^q-onذa^*//߿?M6\Xܭ[իWC݃D"  _Akӧ{>}zPP#yfΜ9{YYY,@QQqϞ=kҤĉ =zׯ_Kutt,YBD[n}C&N^H$ ǎ) HƎo߾SN-^vEDDg]">>l͛wnݺ\$w޽bX",Y$''tgϞ^|-?|3g4iBD&&&ӧOr/&/D &QTTԱc*m-7oŋGhтO>MDnnnlo_xq˗/kjjш#~֭ .ٓ ?vR"4hВ%Kw HvEDgϖ6n($$$**ѣGW5kV=N<ɆT|>MP+a``Ю];-[\~=33_Prssϟ/=sPWWqqqnbkdffVի\K.ٳ5% c"~ˇ FDǎc{ijjvڕŜ9sƏ%;ܹ5DEE(((#2|ZZZ~P(H$&&&wԩ>= ,,,BBBK:YYY ,FW|m222/_X ׭[gU&&HoŋD7 !DTXXA;nݺ0??'NY!I>LDWTT >v`'N7nCΝXA"0g b{bXzO>39"))iԨQsqww "-Bf+Uk7y<޺uvء/XYY5h͛73gάra</??4hP'*))U~s&Mlll޽kkk{)"5j7, N֭---cXбcǿ7ot钶N>вeka0S"T^dddL6ãr1/|--Z񟑘 P}oذF!0x͛߻woΜ9Bcǎ;N0!&&TTTJJJCD-[8(**n޼f͚Դ򥲲]GGg̙o߾ 55e˖ݼyСCZB/Ԛ6m/66>o***}כtؑ-(FFKKk׮];wlٲ]hhĉ------LBD!!!斖\H$bCZ&!.  N8Ѿ}{###++~)::K«W?ʪm۶3448q"G_)aggWeX,޶mO?j^ -X@ԩSM6EԦ TTTfϞdɒ RDϜ9s ܺuBDԠA3f4JH$zyfPPsrr ߧOvfff3gl׮tu3f4nܘ0lذ*Wvd[;c EEE;;ʃ 7o~?iiiZZZfff jժ,]]C]t)--POOSNG̜9k.HU( WWW֭[7:jpnݺI7G&Xc??7oT`Kω033NF~'YmM #*c!*jJJJ^444>|ŏIHH8p TGG'''G: >>˫* y&`~ėt=l)%/,(־rnOjhh;v_Ir}oNLL9rdu1S+—$3Kz+|&5700عs@ 7o:+cccÖXXX=z4==}ԩŰ%K:tPK/)))I) Onnutt177?rHzz;z >-]v sVwqqC $饼Ș6m뫫 hmmQF^~@ n ; cM@~긤$vk׮b}c ???@f͚0fddlذk^/_: ay߾}o߾ӳgO>E -[4nܸʘyXy=_ PݸqW^nu1~ ( |q|>crW\ B~H{{]ְ_zںuk|U&LիSNUwrrzxx}=_ kӦ>oegg} B㈈׈[O1 LqqqJJʻwIII `333WW'OTMqgd1&8;;߾};:: AAAfffC 5/|3]6k,wr۬Y; p>ljj;S &>x𠺀 d bx۶mM40aBup544/_^s~Y׮]߿?pbBCC۴iӯ_߲e֭[7l5߿[l | i>ODO35xܿURVVVSS)5㦠xUhkkuuut|Xđ#GVs{{{GG|>/|{*}vH"| U;m۶VZ3 TXX[s }}};w y)))Ӡΰ8zhzzԩSիWΜ9ڄ7k׮BDٳ]VٳЧOʴ B6X ''G <ȜSN=~+K.;w}~љ3g.^XLzz+W?***ƵLMMYFCZAA@ `늋D"_eE3gܾ}[fZGII@ DQQQl FYYٹs>|Xy#_xbXXXllX,zŋë.AQQ sΝ?>77Wyƍseee_jeGpwwGA]e``k.@0w2h...!!!1/|cﯮ~!":x M8Q&VWW2dH֭K6L4IWWWSS3== tuuuuuuttVZ5niСm۶۶m[gZZZs6mRQQi׮K߾}ڴiA; ##={hBAA᯿Ю;y򤮔[WWADTZZ"t"diiiddҹsg?{u̙{n߾ڬYX[[[  366۷:h]]͛433Ҋ 7nܚ5ktuu pdڴi]v0`?XZZ|=222M~*cß?>j(ׯbCC@|noX,5j(((K.'O>|8-Xڵk۷o"ɓ';99)((,Yݻwׯg>g&$$ 4"11qڵ3gӧOg1D|r}} 61iҤm*++;v,,,EٙѣGڲ֭6lXDDwMwV-Bڒ%K^~4m4%%͛7QM4b-,,hC IIIYbůjllܿDf|}}?1 `ҤIk֬Y`ܹs/\вe%K4lǻwЁ)))kcbb6n蘘va&CCC;wܻw䰀>}DEE,^XMMmΝnnnl tңG߫W/tC~~~^^^6mZzu؈%K,]t̙4 +~_?999EGGsOƏ_]֭[=<WTT4lذdɒ%Vb1&&&rj),,DU߃'L0lذ'N|D<СCտ A_ruu [`ڵkYݻwmmm۶mf@߿DDDTTTϞ=###lӦ͓'O^xwq///9̏tMCCLJƏ/!wޛ7o:v%hڴiD$=E?)((Bzz[\F˧p1l~QRR;vvXrӧW^z*kR@444Xr!555&&ʕ+ DVFD\rlll=z$f``@D7y &`w}PrmSYYY$|W\a;0<ga95~"q~nEܸqW^Z~u1~`|>[Q͚52G[[ #Icƌٽ{LEEEysb18 *֍Wvvvaa!mɰZ^~|r&:t4ʕ+,r}햅 6jެEIIɉ'd|*k׮f誌iԨQ@@@nnMnnnŰ v u{giix<*׈^|9ijjjjjr/Yy<6l.\تUǏkiixŋKw "+++WWע"KKKuuu--/:::V5 [`ҥ lÇvKQQHoggZ&M$̮WZմiSMMN:q/.Z(;;ٙue^z%\5::zҤI</ qG'6Gf̘1<oʕuo uttV\GqΝ*D___mmEUW^__ǎͫ`-"$$SL.&&&gϞ:t8s z /|3|}}+OoӦBKN:ugϞ)))2ol۶mNNNXXׯE"tVZ|REEw޳g^t)[QWWڵkcccKJJ"N 355;w7o*'jؙo޼9%%ESS;v[=,]TQF~HMM={lzz:c Lr֬Y3zÇ'$$sСQOOOWW bllܨQ#"6l)[.^ѣo6jԨUV .Ϊwww++;a֭2oooFiNNN_޲e˓'Oڶm;jԨa\r˖-5bnnrQFja``5koʵ]\\,,,kF+|>NNN3Ən$33oΝ5X[[|[fff&]9"$$x= ]@GG- ),,\vm@@@ |}}NoY$%%=䂃åK=R痛zjn www 6T9=s .f{ӧe޴i\r۷oQ1@$&&fǏypuu}Cw!SN} $M_ZWW}of/^@ >ܣGS>x@l@EEˋرoAڨiii :pi=z'@ŋϚ57ܹD-2kw'''QQQ{\.w###JO ""ss}،F@@ݼysNP4^t)33^R̙30a­[P1yw'N4{!--رc_ѣǏ(''?}vmEE/\cǎ"ox<^pp0\|7of455CCC\uڵkȘA;w.77WJŋÇ|2* ?_Oc2wftuu###딕Q4֫WGO2׮]=zϟ?@2&G={9rhٲeڵNQիf޽;y޽{;v m/%K,III :t!C~g.+)((Ç͛7`++ӧO+(!|}}3zh??zKXXXԛˠ~ڶmƕ+W.]Txɓ/߭5[t@ n,//?~|̙#vI͛77_~ΕQOo߾]p-[O޷o9rmmm=p˗(']\\LoVSS#6cjjɓ?r^.cr~\.W]]]lɓ'ݻ x =sΕ߸qwn^xѱcNJ >Kg,X~B˗/ !>>>ׯoxWeeemmmrVV@N޿_]]R[[knn~ΝӧGGG:MLL;wky3gNOc D^^ϟ)%ciipƌ#U޽;VC#޿,bvGuuuXXƍ߿/)d2}||V\)iUzDJpz pѢE̖-[vU[[K_| MFO=| zKEEٳ;BCEw? >}2o|Ү]m׮] !~~~Ŭ,zRzٳg !]ty@ x!,<GMqrr믄QF ѷoߪ*͙3bsNƍqjehh"!W֖Rg]\2 zny'D...hᖉ'B܊/PE~vqqҥ !$==]VQQQQQEԩ0Z@044?jKb nJYvp _ػw/]\y<Џ`TWW[1:w,I߾} !/^nG£ c]SL,g/ nݺI)5k?u ޽gϊHdee2xa#,X}^[[KRAQTll@ (**zÇ۷?ҽ UK,jIqvvdrĈŸ lٲgϞEW?lիWtwFz:::QQ$zsgz Vڴ6I7ߠ\L4 uz8PԩSNzEoo[n5 m߾} >Z-ڞz;uD)** RSSr逛۶mD?*))VTTЧ Hk.++ŋ;V%WWWҥK³hyjv 6#zJJJr'%j"F#ݻcƌqƼy:$0iӦMMMz$Hee%}畮ǃtH ڊÇ 4M@Cvvv/_!St3`HԌfG\qFSPVV࠮~y}}Ç;88̞=[toLLL$ݼG^zpg'蔔<\x9=E 222 ͛7R^ ^^^bK JOOoըCC ( BK.ݽ{[gΜ9sヂK/@'+6Eqqqχ111QUU%|@|rV^-}FFpH!zv}S4EBjkk% !檪7o|1%!!A~Q>d0 رcQYfBN:uuƚK.5NOݣh$s7~t,+::LB3g!$44^R(33S[?<>N/Iv+xL&s咚 \.wݺuh.@4hйsrss.^8|pkkkIOxu||p0yO:uÇ`ҤI2ŋ+**Ϝ6l[ZZjhh\zuԩ>ܵk4OOȇ:99uÇ%ͫt…ݻܹsɓ'O&DDD;vlϞ=osrr=:dzGlٲ)ׅn}Q:::AAA~ )F8qbbbsnnnLa8u ۷o„ >>>6l000 _~y%zjOOgN4<>>>##Foȑ#RV"88844Jtuu׭[l2 ^z=z4,,, `b3iiiGz(jj ݻwW7:z{=z$)ɓ۷o~r222\nSѣw}V|>?333//O=gg?eEݾ})}LJy[|˗/o߾]UUՔGKff]V蓃LLLP+h /)//k׮;j JԀ={6~3}r ACBB!$88;d2LfSW^_yҪg=zzzzzzM-?--??ȑ#R2AAAcƌAUi߾}``  ۸q^zt5kXBk~dzg2DG@kPSScaaoVVVCϞ=4ƍwƍׯ5k\nxx.<]z5ܰaCkEz:vhaa!߱cǦ쿲2%'Oī೥{{{_R2SLp8 @MPPPXbŊ+8nݺ/^4|wÆ k׮mZD ?sɃxљMMM޽Ztҋ>4RRR;y󦔌wrpi6#IWWW???'@ˑ#dFLLǏ/`0\]]KJJ:uӦM{abbao߮=|WN"mÇ{1u xyyq;vt1a„իF9pӥ v _/7` y<^qqqY,VSիWݺuk}qㆂzXR%%%UUUݺu>AmǏ߾}s> ׯ_KhhhxzzzzzkYeeeuҥL6]o1TӧO>}z„ GҐh^5~AUUk׮]7ߴGnhhU?ͻo#<<\z~ԨQݻw/++kъmhhpn-3˗Kj.hjjp\???4@ <ܹs999f͒pí/_4̿ж_7} JźpBRRի ! KK=zjnnnL֩ߟd޽Սr(ȕ޽{;vpɒ%2iiiG6334 RǏokkkjjژ|LL̝;w:u{ET/)<==Lf@@Ǐf> Mڵݻ)rww̜} {}uuWTT/VVVRU[[_|;((mmmCCC^xqܹ6lj ^wUUUEQ߿w~*++_PRR"v7*++SRRY0Кؼ~1-[p\uuuٳgٳhfzD~&NI@@!dΜ98Soݺ%xMϞ=~IWWW`HHH 8;; Н===ŋBE;h'xyy}Dl6[Q%6\oŋ =r䈆< IDAT]v$x<0VWW?^PYY)'Ixxxu Mט~Err2j\Q覦 |aUUU7n>2ݏANoKPӧOܹ7oĎHLL#㏶111W\>|HYbIǎ/^w^ccӿE___AA!44ٳgtK>L[[ۿ`͚5;w={vll,[r%ݲeKhh(ݚ MfdlmmFr|5kvXZZ0rW^nݺ__~4 -ߎ_6m!O{DdB]](ŋ BȪU苉nݺ  .Bϟ/BݣG{~%ܲeB]ݖ>~Ah..]B~wэ׮]\XXX&s%ر1cBrss2]tWlE)G-vZy_>vXoԎG hm'}n; a^ZIt?}PFFƛ7oƍ'HOvmbJJ ;\\\>{͛'1Sz3)**ҥO?$Rs׮] !</77755L&'NB֭[':bѢEׯ333 !t#>Orr%ӧ111 @>?~ԩ~ TWWoڴdZJ`$іГi} Ч ATAI v.=_W^ڵtIp,Xox<^yyy3!4 3&##cРA...FFF3-+F&=& }왤wK &L0a„46Mmh6mÑ4APUUUPPNIXo !?t?t'Xtc悲2_yF_ --8z~~/222tuuhݻgԨQRJWTT$QAddd``ׯ%e444<=====Ѵ.]f"tӧOO0 6 E/% "Xoy:WYY)NzECE?KOjj*w>!Dѧ_~ӧbna{^%]ݻwKuuvUTT硈} xL&s咚 !!!\./iΝə5k̅ nmm-!ЖBVX+B&Ҵ\\\>~hmm-p!++ktReesڵkt!++S.]re׮]Rz*DzFQC !8q"::ӧO>^[WWGmn_BFAo<-3=!%%իyIjFٳg>|*///MMh___z@Gee'NBٳ !7or =ȹ &믿{NlFWW722zxxݻc ,Y")6zh333IS~Mv}J@`cc#zzMG^OOOaWuСP@p}JzDc555 w;,,L4Eoʒtdlͫ'MT/лwoaˋ(իɛ7o צX; :PQQQ$LO /022F@Va}Jh(rww*ݏ@ꑯݻLO@100~V\|9<<իFweff& >}ҥK999ʆSL~O{}СnݺmڴСC\.WEFF_tz̘1򋿿é7mڔؾ}{ 6~ .w҅ޮ뫨& ?ٳIII999*** Z|0yC>}͛7]t矵9RC֦(*&&ɓ'uuu555@X[[?|˗|ЁveeeߕӧO:z)@ܹSJĄ888\ܲeK``͛7Ξ=k׮R<L /޽{+uZZΝVlٲ]v9sfʔ)x!9rDJ‚;>>Ϟ=PQQqssԩ &L0a„46}e߿ڴiAAA}E@iADD悆?ݼy3 ҥK s~M8ۨ6x!!!L&s_ (ϯ]v(>>,(|I_;vlIIɫ$''>|ŋ/"992%%ER)???&&fȤ xɓ}ڸq#ZjUii)*_Ώ &z1..n( @C&&&ƍqpqq)..>|p=P1yӧOQBBСC|~xx… P1hi/|Mҥ薕+W2BQQQFFFSL؀'EQ:::ȕ'޺uի6662֭ی3Q1h9/|Mzzz7o1 ڵk3@S f W._1i$ISNۨsx5+++t@>xZRR"6LQ@vXT ֮]KQԶm۴f(Zz5p8(+/Ft(55K.k|>Jmԍ7lmmMMM$eQ1楨rʷob3>>>,(4CҰdhmrss;udjj@ےfӥd̙aÆ=z\m]||˦wEYZSNlIEE˗J mȑ#\%..VRs`GEE%M>ѣG C۶m^paAA*R-%::gϞYYYb***EEDDb_ĉoݺjcc#)o߾nݺ͘1C`s/@3NNNb3Ekjjh_˗ܹ3i$ISNۨ#_tKKK̛7G@3x!!!,յDlFSS388(???  U133;ǏgΜ))8lذ#G^r!%z\.W r͢"444$$BRFGGgݺu˖-c0@kfll|Bb3W^5jÑ2Ώ&)++bX =z8|pqq mޞ={JKKܔ1aooߧO'NbrL%%%,+$$ 80&&&?? hX,VXXEQl6[]]]l&''g֬Y;ǎׯ/--СïzjIC@W>ۍ7lvrr111A rb߾}~~~jjjk֬4d _OHII\3gN^^^ll, c/^8yd߾}6nbܤ vـHgjjjkk+(R\\eddȡӧ?z(!!aСb|>۶m .2:@={:::fee xzzRȹ'޺u+55FRf߾}ݺu1cFvv6* {_O DFF:99hhhQ˗ܹ3i$ISNۨ,A!ㅄX,WWגMM`544P4?9sLbbaFyT @6 ***X,WYY،NDDEQ***(|_|xbIW5jȐ!@[*++bXb3=z8|pqq @_۳gOiiLFF}>}N8]/ȣWWWf䄊@SX0lLNNάYbm ӧκ@l<)))++ҡC 6PԱcG%KhiimݺEhC_ߟ2eQTTرc_nkk@KPSS[v-EQ[n(ݝbq8J M@@ݸqcܸq&&&2KNN@)**Z۷񇁁Lyy~=ʡ RRR,---,,%e̙kbb`/^>^JfĉGҐȅ=xㅆX,WWWIMM`\?1cLbbaF)}| ІUTTX,OOϲ2;vP驢@bll|ĉ/_.^XRիF2dHBB*&yyyX, =z>|յ6DOOoϞ=nnnJJJb3&Mӧɓ'Q1B L&mNIIY,VHH8p`LLL~~*bQffrrrfΜi``w^T ڴOFEEIɘs8[[[ "-j˖-7o~ ]|!мp~@)SIi.;h.SSS(j֭,Kl(wwwp*++Q4@vq 8066VRP IDAT޽{EEUVݻW___lLJbl)_hRRR,--͓%e~ᇼXT … N<٧O FQ*_huLMMmmm%e\\\P1Bӳ "6mۦhѢT yЊ9rgϞYYYb\.7""BWWW\9rѭ[3f<~h./ ;wҥ˜9sڵkrCBB455Q1)Fyʕ;wIʜ:uo߾vvvwA&44dti\._CCEh$33ǏϘ1CR&11qС666@Su|1 dzzztyǎ\.KEEE 'N(((XhLjj͐!CP1σݻ_dWTTtСC%%% Eh"}}nnnb3&Mӧɓ'Q1 /˗/g2<Olf111O>uvvF rjjjb39993g400ػw/*x/| Ϟ=;wNDD@ 1bDRRQ1ӡCr9Nf^|xb--[| _hY<2eJ=>,)3vׯ߸q2Լ\֭[Y, EQL&3(( Erq 8066VR޽{jժҽ{͔l&fQ4_h~)))ɒ2?C^^^ll *-\ĉ} b2nnnEbWٳ 8>[\\mzz̲eˊP1I&fhf̘?d>m6--E|bHjN& /޽{4#GK (++Zfkjj\ c>}$l+PCC aeei& ZT6*%3c o. /@ܹ300XR]v(ȪСCI|ROO"##;!!AJΎH '0D7nܐqMMMhh(tqq\ԩ͛\? 6nؘܹs\,>>3f̐IHH:tsέCIAVWދI([~}ë>~d2===޼s;vr^^^***'<==s~26@kcll|ĉEIʤ 2D`W>|xٲe(&*:?` Yff&!SN7{.((h֭RSݻw_~3j򦰰Pnsψ#8θqP=sR0`Ȥ-[lڴ\RFKKKWW-;wk߾= W^c̘1AAA(M ^wHQT#obkkҁ,_UUe# }ݽ{RRR\%vG,MQQqժU{>Prr?J3f:uꓱ~aÆ FFFX 0`ȕ'OǏ2dkkl \PRRZlYQQQtt4 R€ of̘/QSb 0~B#FHL61! rcǎ3Ə=RPW)-??ƍ555(>}:""CmknܸqxEeeeBB Y~.]$ex8x$##uuurx ƙ3g>ə={)Sߎ;NzJ`@Md>mʑb|ry.nK,AY䐚ZKb\\\uu5J!¶n*-D .-k``p⠗C,WWWWX_;x'4d| MMM媿B0#4 T/@S zZnp~Ν;h:/; :@T/@SM4 T/@SM4 T/@SM4 TJrh\544p4|]w /_GΏPP?WII+-/@SM%_'vYQQ#䫿0vؒ< G@SM4 T/@SM4 BK`(((0f$i rqTUUUWWյĝ+**TTT4&i&*]v-//OJJ7=zOi$6@iZ[[o>BH?M% *T- n߾]YYiccӯ_ٳ (((ٳ'''f9rHOOoΜ9&M2ݻwgffjjj.\k׮w7nܘF>|d9)))/^ ۷o(Nj{!))?>11Ç,kРA WOټyÅ ~z޻wȑ#666=RSStss ***>~￷VAA!11%%%={ݻڪ*???z@ ?~1c***QvZMMM[vݝ`8pÇ|>nnn:::xY\p2f̘s]rF4@Cŕ.]\XSS}Ν;1b޼yL&S>|wuuQF  ̙37oޤ/}5u? V @TPQQFY2l:ݪȖ%V+>DLK QQ*CE(J 6 w.br8=rogׯ'%%M:uٲed;vݻ+W8qxȑ#%%%cǎurr|>kh/`c0iiiA׵:UU&]TTd2p gϞ-^ӧAVUU]zOdɒGikkܾ}{Amu'O,_|ܸq7zbWWWrFEEeӦMATVV=wܛowYEEųg&NhkkwΝzx >|_xyiQ@```@/D33+W #))ɓW& ƯO@ (//?}trroDvIprrᅮ L&sƌ;wVXLQQQQQQQSSSXX,{Ԑ7nhnn [ׯ_KuiK/'''._\\,wDL[[ 5jTvvvvvvNNΏ? ~3fw08 -ɼ|.˗/~GrssΝK?}tƌn*..=z3xK.gggq%.+~tÆ <033wuu-^ qaÆ9993IIIFFF.\rJee… ݋`edd1gΜW^A@;{~zzÇo߾eqUOJJJNNw^YYYqqi:<<ښ Q[oUSSSQQk׮P(\lAoFҥK+'O3~֭[wލ۶m= g~w ȐuzIIIٺukyyyMMaggGTTT6l6::bI<ڍ7ˏ?N?,..DݻwWTTJm fmmm_zuEdVVH$zwx ݿ֬Ywܹ|rYYل rss ͅG૮~޸q #i3fDEE[<o̘1۶m#+7o,((6l؏?񜜜"""$644xǏ;w.ď )uQH$$ԩSӧ QWW߻w/AFFF ::ZEEԶ@UUU_(ٲVl:ǔkhhxyyo> BBBON'uuu555?>u+V oذ RΎb=y򄜹sNmmɓ `29v˖-<c666w;}|MMM6=nܸvY.]=lذ.ssu֑555#G'[zuvv.^믿蕰{Çx9s ʞ={&~瓧M:_p!AN"ϴ***dMvѢE5gaÆ%&&A$''c. 4:ٳ ʢ^-_gMCwss߽{ʹi A466Վ3fʔ) ׯ_'BMMi?&_˗/ hmm=sΝ;+ `jii)++c2AK/^(uٳglP| < ~:uv@inn[ke  &n f2#BUUܹsi633sqC\GGG>e\ROe~)L(>m޾} >HMӦxaP($o%T҇~HDZZOz*;;[UUu޼ydYx522tҔ :À0*AGSSܹsgJ=K} d53.9&OnWWעEn߾baar6mmmM^ԐV㍍h\abb#TUUlv}}}vv/NvYd8M==_Ub-$6P***d鶪jԇϟ/AjF*8MdLLL***ZZZNj``@?Z(r8Ǐ8q⭷ޒY4v-r)Xuuummm}j6E"9/X ,ZO?>٧S|zp8A̛7oɪReG/;n(by)Sjkkb.DUUUS}֞9sYRRB6_N궡K`h "##ZZZϜ9#voiii 6իWq}A.Q޻wӧ裏_BSSǏ I&-~$^1k,r9ytwv'LLL(--UQQG#܇#F9szӧ/k9z!3B2 8?׿/k׮%ٳR;Jegg7bĈ;w:uT`0 ƥKѷmO'>%^t u(CHII$<;AAAABY=z/ qWX!>g~GK,!ܹsߤ͍ 3gΔdzsid2,X@… gɓ'뗕⯾TTTÀY!0hO،3rw;vloJMMm>>>[l9{Y[[[FFc/ƍ $7ܷo}3gGzxxlݺ EQB0$$$##ĉ7otqqQQQNOO777={v/-#&)ޙzΝ{ԩ kk|y% w̟?_EE۹彼VQQ9|0<^x˗AhkkoܸQ|׷vANNNÇƗ-[?޾}a…#Fhnnwqnd+\{߿]CC7Ofݻwŋ/622jii?<<ZS(:u*===88g[nYfw! 깚<ߊgL/Cqk" IDAT|>?$ZpīJMMLLL>>̙3/]?>Zt)"O#~$O"HZsPHƌC3'UI<wZ+B ħh CUUÇMMMl6{ȑ"HNj&sb, #T<ަS #AO髫K|`0444\nmmΘ1cB `I[|&JPu_xQ]]:jԨ#F tϲދ׬Y#. 0xBrWW)m;;;x sss[ǚ q%-~;?~sZӯ@ R22Ft#ݟIg!~򮮮#G t4zY xZZZZZZD3Ug3aϯkhhz(74B} +P_B} +P_Ro3 r`}D7  3<6 W꿠ࠢBǏ#U_ppp(((п> +P_B} +P_B} +P_B} +z&Mw/P8#P_W}A(R8NVܹs?~`0Wׯ0@[CCcȑ}p=! N\&x"-4$:\S{{;u(SPhF͛$HD#-O< QWWG86RᜩLP_Pf3f@;GGG$̙$@wG>|8 H[[ySe2?<<<>|͛ [vmHHRb....]B*))5kbl6;&&fҥH`vhvĿrii_fff1ƍȑ#O<0}ggzMMM۶m;v올9s$$$yVYYijjJg. ܿ7\r%u&&&CSfddܽ{w…bǏ{]r ?Hb֭['`cc[RR2p X,ѣG+**}.\pww!8[׮]LM}@x8'Nxԩ?\Vf͚u' ʂOssp''yUǏ?sLaaiӤddd|嗲^3к}6u8ydh蓱cǺQgh=ߴi:::Z[[geeh!*{{k׮=ZjC8ή]9PDܿUHHuY壧wm۶\뮮'OpВGMMMBBԶ|>?<<!Q_@w~`oo_\\,TWW0{ٻw˗/e~g8l`D&&&f uuuEEEx<񞞞x`ȩBƍVVVH  \]]3&߿?22MV ܰaH+ TmmmLwa+uBuuuԙ.UUUd⡍ wO~mhhhSSmmm555B0@M6>}ZVcBB<ˣnf\.4A eee~g_Ò RQQill|xȑqUU2=ye믿r;wd2\455_~ر8l`D: /k?~\|p+E ömكRZZ """,,,.Rʽ!Eccۏ;&+6!!GrŰحhSB?[j? jhhH ;uT1s΍EިꫯΞ=++5!!G'OzzzSLhܰ>Y@@u␖M81%%/sy;;Yf#c7nܸ3gZ[[K ;v뱔 /͡lرnnnԙ(7F?oڴ`H?1ZAe_z555u̘1R>fwсt@I|!!!aKKK`Aށmۦ.5뮮fff2PY;&$$hkkwT s8#WlN t 0 틋Cjjoo߳g޽{_|)+f̘1aaa}|guuuEEEx<" 7nXYY!-@/ruuΤK^xmmmblvPPІ d-ł€jkk 0eʔGGG z:ե}ԴREH$CeA|'\.w޽ÇhGGǮ]lÇ+#P_C駟zXť=|9|ȑ#?㪪*d `h+ 7oFeA}\.wΝLˤ׏;6331HTWWG!``ܹSb&""i3f---a'N?>(//G* bذaW777[[[l$ A"-@7/^r%uy޽h"Y1d׆{Eb+++͛'5HI4_'z/Q\~G ب!-jĉ)))555_|񅬘͜9… AeAэ7.++Zj@ffرcׯ_t#._fq=anFM8GHHur1ep8py۶mRc_jffo!c}ʂrwwaBBvGADDٿ?r@7<@666vvvԙ0e0iiiٳ)007ސs݅ OAeP >|֭III}H> $0B!u/=ѫp #`̟?ԙ`evL~Cvm`X{Bzm[qqqO>e۷%%#|fc!6 ...uuuO#`0Il$QRRRTT ?Аhdd$5e۶m,+,,իWHT 7oΞ=[j ͗.]ZWWt(%4_ a} >LGG:[ի=zljj*5ŋ:::~~~mmmB^^* 4aaaq…333[ly9d9% TaVVVEE",YrTKKK</22bm޼ Me֖,8::"c|Ν_~fK p8|>P؜ *oooMMMLhh("?<<<ʲպu>}P\\}'\.o>|xG;::vp:\( JCCLJ:?˺LRXXXPPMG9re˪1Pz,kغu+UQQhss_~ibb\(4H$]#P_sNE988斖ɊIJJ?~|P^^RBebذa\.wR>|nkk{ @AI4_ izB}U3@fo~]YAvm;w.Ab%&&VVVΟ?_j@II[uu5p$/`q 0Cȳ'Ԭ\RVLvv̙31Ph,@7n\fffQQԀqƭ[=tD,:C}-H=zc뽼Lg˗/;99Y[[gee!cpPYfggwմ1cH 8rǝ}Bb}6:C}FHHur1E!755m߾]]]]j]]]N>B@eÇ; 6~ @a}C$! 0$C;U8111111/_3f̘?$CBBz6&&ew|>?**j<OjÉ[lr L&#UYY% ,WWWLZZ;2pmmmblvPPІ  2rmmmqqq&Ow8Cuuuԙ.UUUd J⡍ 6 P\Bo mll㣦BeгgϼRRRd̙3'!!KJnnxf\. 0Ò P,L&ۻ!11HjLKK˶mttt^zC['Oyٳ\pӳh@ +VH8"ZzGMMM … fffRNu("‚'''Y1G122Zl'k= 4nr}}}UTT?_ddd WL$IC9`ܹSb&""iQ>ۈ$$%%~T@ 6,""YFjÇmmm `h@!tFP(dQNhkkZ:sl.~w.ZHVLjj{W\\Aʍb9rj޼yRJJJ\]].A#$Ы;\8~89@x8EM81%%vʕbΟ?ooo?s|d zرcO.5 ++kܸq֭C\vm#䙇Gzzx. 4rA;N9| $#Yzzƍ>|(Q;vhjj"Wɓa```HHt /Öcǎ!-tԴ}vuuu1fffOFƀ{PYzh`4y䄄 ޫ300| _LMM+++ӧ -$ FY1>>>jjjȘrCe꼼k%̙3'!!\fs\h@QW^Za2 FҲm6TB qqq, }zC__?99|Μ9R.\`aaYWWt64_/+VhR%QqZzummmrrԀ@ֆ) PKvpliiT6;y򤁁͛?t[nQ؜@}䐟ux̙ ,Yr4+++</22+:qea˖-,6''۷o?aR8ݻ|>Hܿ5G $vN ~ƍl{{{ܳpݺuOرCb&22i 999nnnbLMM?rdLn08 r׬Y#5Ç666H@$n^ i@}䑶U3x2ݽ[hTKK˹sYޠ0X,֑#Gϟ/5իHT<@*@NP</..iY&LR[ IDAT[rJY1d׆3g#cC5v̢"YYYƍ[v-(t'qGP_9ebbNFZgF:vXCC)ve''iӦeee!cCaggwʕtcccwPIlN#H HDϟg0PGGg8VII-u&11q FkkkDDDlllggI&EDD,\B0>>>$$DVY [[ۘߡC|}}>:|𐐐["QAXZZR[;8qbҥH ԯ-,,n޼@988<@Zcbbbbb^|)+f̘195pzSY3g0T|~ttݻ;::츸?c2ԏQeeeH G\pȹÇ7 7|3889**JKKKjLMM_|p2eJ^^UWW'q$|6ok׮;w"Wܜ-Hۍ7"-GB6luF#kswwqFNN@@vmXv'OpM,[۷oxG@I b/?~ \XXXPP,+&11hҥ ڲeK}}Ԇv_}1{e% 3/aǎ3H /7nɊINN655]`Ayy9sijjsܵkJ 𰱱)..F@HlA!@B}U3x2*==޽{/fii9w\^:$u᪪*WWWW^wuuB@iHy 0C0aɓ'kkkW\)+&;;~̙t * رc322mlldee?~ڵXSP&&&ԙ(ԨQ;dJ?a^|iڴiʚTgW\IOO766pp!(:)Q_B}ĶGEZ`755m߾]]]]jLiiӧwGezNGAdd$ٷor /!P EEE⡱4{ݳg˗/eŌ3&44?WT(LJ*+aggHѻwf>c ɤ~*++DZH/9s3iii&ZggglllDDDkk6qFX* /?++,!!M]]uKݫP_3a„ p%%%H >PxFY1ZZZ7oVSSS_uuuɲfϞ`aa\uqql6EZO`` uxBjhh8zQƴn߾]GG'44%C}`'%%:u\xrɒ%Ϟ=C@Ρ@P_ųb QAAAH UV֞, sttu։'8Ԁ8{n>t}6uu=P_5l0֕Cƍ999RA\\ڵkM0ɓVmoo?s̼~GQYEabbQ\\lcc#5̙3Ǐ_f CP^PHvvvPh&&&GGG#- Fuц///&SӦM|PDW\011pQ6;aIlN@w/̝;OrNbۈ֣G"- tuu㛚o߮.5mҤIOGO. ߏ(.WW^GGB022|7 &ܿ"Y@mdll˗/ٳwvY1G {~*P*@]LL |~ttݻ;::O>Ad2,--*@9sfԙTd=$""UV^PPƍ C,{y```-z 3|>_t( &TTTW^EZ@ƒ466ʊ!V4rd̞=;!!"l.H k׮"-pLWCCѣGG%5u,+88xϞ=[nEPzĉnrttpEKKŋ?{ ~ (O?bQg$*eժU'O0aԀ;v2eʔ>ᄅ| ۷C P^^^Æ #-nܸq9-- ҥK7bĈx@6w!Ww#pT//MMM (:P%K'|tJ hb˖-\.Oj' 6!WڄBa]]uP_cYY蹃cwɦ ,y&rt^__n:555 ,>}zQQAbqAH @w/^z5u^Bf@l2Yϐfee#@:::vuup5WUU!]Hy,/R ;;;cccP ,_p_]jgɱw[+WJ 8s׬Y؈tA/K/R166vwwDGG#-z_Y(,,={693jԨG644x{{3OӦMDlll322LLL=z>G z P6!!!akkkbb"* TqqqMMM;vPWW nnn&M:urtZ]]}! ¨(7 W3#P_!P2N<@Z@ ¸7H-+H˘vY1G /谰l6;66O>A@*&ITVVfiitGBYYٞ={ķ=:<<9s3 ȏ,PG"""zROO/((hƍ (ϟ߿_VYBB3rTϞ=-KU_tssnܸ@)M0Bl+ T-w^zzo-5EGGX,&}oݺps8.$6@NdW?~\ߑ|W8ѡk|Ǐ#308kyК5kBBBFw^lll``ϥ>Fz200 S2466Jwr^Y***!3<<,(ݻwGGG|GNHHT" %xv v)1aoo} wwwY1&LX`͛7ܽ{7]~Ԁ L>  LKKkԙPWL,PYYYݿɒ%bҬ}]\Ur﫫S]va޼yHғ/#@P_%@vvv"-Џ@ejj\[[j*Y1999N^^zPb&&&W\pYSSիW766"]JL)z(9cccJSY5jѣGp%8;;O6-##(1⌌ ǎp8;RY/$$:lmmMLLDZ/@Դcuuu1&M:u PbՇ"Z(FEEoR>/#?haƌ}>>'N0k֬|׭rssu"SWW.k׮ -3O1L//cǎ=ZjLkkY,Vhh˗/qRp8[\tjѢEO>Eԝ;wClN P_O%2#- * =[reMMMJJĉX;w4eʔ|sssO9r7΄֭[o7r3Fóg޿i ,ޢE޽oK x:::>>>"՜9s˓8pᄅuuu!] DbYU$C}hdӦMÆ Hl] 4ŋQYrss+--uppx==k>~dɒgϞ߿Ĉx'!!RXO4yf/Ԅ?,̚5 ɩ鉉F6[P&7ormiiٸq1cҐ+9' %9Dss3u՛o9m4ڪM݋V/ػw/ WVV痞CGDD:jnn=t萬iӦ%$$Ⱥ \yy9R C("-=W}!==C<2eDk&&&---&PYh~~~'O!%"" 6ddd xƏ\ɛKZdW}kvEvvv"-" bb@&''?zhղbrrry睼1-gf^{XZY`>}Ԅh5OOOfBBBVVuԏ,\v M6u̸8 ]]]V<pСC߿պuk caauVHjPeq`@|ի5kF߃vAd\\\޽訰T* 533[dIQQz d/^ؽ{w˖-oG_5&@鹹=z =YÇ_z5%%EEknƌZի-[͛+WƢT*}}G v=;v@h2Dʀ.^x޽'*ks ;;'3 mի˗+lɓɓ')))zH^x*@@-YgϞ= ȂݻwlllVVӕݻcwqqQΝ;cǎE&W}ZC|زe }S(޽ݢQY lmm>}xbem3ڷow֭+l`ggh"Ծ;F|#ԇѾ}{F굿?EC VVV%%%k֬1O>1>gϞر#_5 |}}薦`jjkwww}}}mݻ7a„.]Ġ3ƍabb"T* B_ Ɠd lll -MB"lݺUYX "A|1((( ÇX[[.\!Hmۦnٮ]Ν>O֫W C IDAT/t :_? cǎ믿&MiL,@ ۷8y̼V\lb{Ν۷OZ@KjbX gԁgooEm~駷oF4D^Hp__ߒem<==c ŮǎS`{F_LXXX|&'%%q͟?gy{{7SSS]nihZСC ۔pѣG322+WL3t >obqJѮׯSRRݻwqݜ9s_,@Y`Aaaatt>|111x-z 8[no=68uꔕ՚5k0)s}&;&OOO 3& cԩqqq TUUbx''8qQGgss[D"t#AYB|^Yf=>>>z4>w&'';::*l JCCC̖,YRTT6mwݲeK[BExx88%GԈvR)}S, ===777GghZÇzjJJ"l׮݌3c[lё͛7+WE_!J_zE߃5]G~f-틓[jE߳~ Lam4鞞gΜQf„ ;v@uׯ_{zz߿_Y>}۷oKի?OjX@KFFFRzzzo޼a̛u ,;;ĉ*ڌ9rǎ @wX"..NYѣGjgDEE}Wf=@5_P`˖-MPk.tK`6h>[[ۨO.^XY:::&''lllΜ9s֭+lp;; jg[_#h߾ĉ{-jBd*22d͚5@ajC>}cv}Ϟ=۩S' ~ ww?jU0 (PS/(K,//@iHHHYY6ݛ0aB.]bbbcvƍɉ411U*XXXi|j4hеkרMtB\Ǡ>(kӮ][.\l'HmVQQΝ;kwϧ9JKK{j!TBBرc{bcc&*P}v綩ʕ+|{Νۇ;;;s^|٦MXl(*YYYf߾}SSS-$u" FwKd0__ߒem<==cjŮǎS`\799yȑԦ˗/qJ~oPۛy NGG˗/_z`5jժCY[[+lS^^nbbmc͏=#F(lp/ٳg{>obZ@|@9s01"Z ,(,,WÇ>>>*z4_׮]ߞ={*lZYY^K(P /Tӓy ASŇӧBU;WprrJOOfT%ݻb֭"УG5@5V^ݬY3DƏΝdGGG Rihhْ%K>}:uwmhh(P(677 c+e,N)--UX4Ǡ#@nܸw%%%h3};vڢ~Q,+lЮ]ɓ'IRRϟ?W z=۷oKFŴ4מ8qnĉiii1`)}}m۶zoUӧy߾}X \gddd{VTTp"P zʚ1c6qqqׯ_GKL0Aaw4h̘1,z];Ԏv> 5mٲ) wű(HY5[[Ǐ?}KJJ8pcrr2z X̙3o0`.\[`[*ݢ@Ю PkϞ=q&OLIy/M"޽{۶mP_|}}$6{sqqAw{?~Ŋ ox7nn޼&ӧGGGS^^^85] <^<CFGDDEQ9 6l@@=211 )++W&--m„ 1111`cDFF*,}-,,,5wM,N P/@]~)c-~/F`hhG2hBaiӦY[[:t=,x;v0V&}aӦMf>̏/#U<Yhd͛7*++ 022Rӧ-233۳gT*EիWׯWؠd]txF=/_2r+PvBJJڵkm;w|aP#YYYf߾}SSSQgd0__ߒem<==cFnnnGU`РAlGImZXX|+PwG;w.}իW&4" _~z䉲-Zظqƍ5(2=Zbr⋰0KK˦}+W6%~j#jfΜ9Eޚ1@3-X0::^a>{xx h]&%%]tgϞ VVVWnQM_5jӓy ́:ujfff|||>}6 &&&lYFnȐ!mڴQ`޽"I!//ի5-v?~;wMJfff/.**BL:!!! Bss󰰰nX<@mxzz6CqㆧӦM󳵵EwTUUm߾O,+l`ee>yy>RNzT P سcǎ}JY'&&M8QYh;;']nZ\\*lPTTڵk| <ZC|6.]J߳wފ&y2,pO^bccg̘M\\ȑ#_viժUxxxAAXݻw 4z膮rň/tG_-[7B]9 m;w>~xQQђ%KINN8p#VSֱ}6HLL_`AEz@-L4' =,--#""JKK]]]))))#GtppG|)))Νԩ677wwwc?_ jLJY^^v211ٽ{wYY6iii&LF;6'''22RalL`aaX?`qJz@}駌oNMN~E}=" `hhWVVۢE mOnmm?]/^+???Z>lڴ_d2DB߃SIBBرc{LMMϟ?O۷o[lYG O(l߾]@G+W}||5 5jTMF$u޽[n:tظq#VD_+{{{zec===PHmD"Z9" L& -))QMOO=,R\\vQe  }~?›tttvڭ[ݻx<Ap|}}A* /_Vq xV*.._)//www711:ڳgЫ!͏9ѣ#F(lp>}|EEEW_}X,O޳gO>CP#/IEEE^^^-1??7Yr " @?~aaaLL>|566vwwW6DGG3|4.]$%%]tW^ >}]vW۷R{zٮ]jSN|Ѯ™3gx4]t7o޼YYe Bɉ )G  SĽӧB0 d͚5{zz޿ڵI4C IKKn۶{'X M6P;U0|ŋ5]Ʋ8v%ܹsd " PƏΝ E*ٳlpDD\\YMfǎC7:ugBBB o aaaADEEQC=*|xrr2z_@|jX[[2^V鞞gΜQ_ڵk%'@ys߾}jr yY|~Ia~~1k~T*o2Ԕѭ[\]]5h֬srrR3Pz=c 5ӧ_54YV 'NNɓ' Yf)lp1jM F***z_/ݻwGFF M T 4Ν;?~hɒ%괏ڵk 4ull۷ eii)96y[[z3Ԕv͏h8 1 0 %%QvAA"l֭{6Ν;V4 +WV3gμy~7jeAAſ@ڕpv횝ݯ_?N*!??\IHjN @C311ٽ{wYYUtvvVQ@3fر,[x. ͛6lhт DNمh!42CCCQVV6fBaa!8 ?~4iҸqݺuCG̙3grrrx<zC3  z]__O|xA,,h*]v]lT*EWh,6,d/X~@G@ϰ`A6mZee%AY[[3N:rhbԩX@3=ytʔ)ծ&p@a~ PW/@]!u]X[[o߾'@iW|ȑ#f޽_;̏B| +`kkEwPw_Q_u PW/@]!u PW:Px<)WM<"8X!_.B`300DYɓ...cƌigN^ H0C&"##c߾}NNN3f̠۷o-ZD⋙3gccAWW~F(-=<<ȷL&[|#бUVV*IOO[ .tvvVΎԩSx<ޙ3gRSSΝ۱cG:5jT^^M ,x"..pƌ%%%ϟ?۷/@1k=FKTZQQŋ/_cBEEE=/8999sfܸqD"o߾}-:8&;;{ر oJJJj߾=nܸ2 " R<~ -[Ǐ ɏ?ޱc#NDt#CA|APÊ۷o333/]tҥ ȇ _y<ޥKN81bꅮ.A={466ҥ tvvv{044<~'0d֬Ys/n޼^m d\-8X:Tگ_?SSS~]]]z]\\^Ɓ6 nݺ@ 8x𠏏ϡC 4rHϗdƍsqqH$ʦ֓d22L~*5U\V@@$P1Lg'J!ka '#{d2ձkӧϱcE!R*л<:*%N*RcR&u|rxxڑ#G]'bN,A̓ELѱcOsn:'N*.sW{m9r u\~~~<O$1lɈG8X.jm$HbX,7o˗/80fr&dѣG+0vرC;((ÇnnnAsȐH$ݻwwsskݺ5~x<ށ޽ժU޽{/ZOMM7n """">> XnbbBCVV>|uZ[[Gr&Ǐ7o|ҥcǎ=jǏ_,YB{QFу/0j@]nU^_|E>l>|×/_vm̙ w';ER^ ѻwwdɒ] Tf?BRܹsMPM͏6 (߿*(ڶmZ<OII000֭{-5B~Wv.\@Fb֭[+щ?|FFjpFiի}-ZۗE_$OI?<<<͛5P(|xРA/^~Su_u IDATz5iҤN:;gϞMM]ݶm[II… m\P(J</ܻwo޽汱'5Gj%&&\LQ600xɓ'Õ͔Z(//?u5 :5?ٳgN׼yj_aaaA_qqqVVVӧOA͛7" VZKD6mrrrRRR . תW{U'syyyUCߣGG%ٳg'[lZnWPPvZr0z͛y<ޡCxƍO<9x _k666dwy漼իWKR<4RTTԜ9sܹQFIf͚M4jRRRbggW߸4߅ 9q>tP>޽[fM~~͛7Ϟ=۬Y3__7ohX u);ٳTiFKe*++]]]bǏ~w <<ٙJUUU} D"|/ySN˗/'÷7n?_駟2;::Bfƍϛ7/66/D|>ȑ#A̞=[@l?3̝;#Z~.bbb^zgÆ dveٲe!!!QQQK.ժ+88icc3ydFK_YY_Κ5O2\FY@qrr>|89d*E\͏6 WYYN3k֬>+$/A:::Aуظq'矏?._;yxx[K. "F-ZhhyO>ر@mzBf )999Sk1A:::.\ x< q^^yC \\\#c.rrr6lMX" 6,::://O>0gz8pG?~ܱcGXL}W\};,]"Z] y|[n}YӧO%'5 CCZA'ښ,|,j$88>iӦs@hphΝY oDŋkPҒ%Իwz?JSK z S$+ЧOfϞ=qTTT۵k'F]1 Ym ln<ŋASԂdB_ޙN(~ӧ%Ʉ 5k#Gߟ~/7gΜ9s b-\\\}qÇ3n-));FL"G}ݹmySNmwޑ%ZRR,,,LR9ըы-/nرc-"TʸF/5|8VCCCt,BT*%#vmooϸ͡G&&&G1Y K533cǎ1d2jҺrD3gΜ9s/d2Y˖-GZkYLF϶֗_~YTTߣG/_coh K-ԝT*-**J*ݺu#֭[:::5v*77W @@fFtСFOh̘1   ;9@۷o9߸\ҩSk׮}iLF%zR @K)/L/: ;vر#Ju o7hS۶m9jmij􈦦?ZZZRUѬY3KKKSSSFZٵk@+ 52j^9 oooruCNtuu߿˖->|Cj]|pOFA0 IHH sSy<^vvvJJ AQH$rIrAs@4 @ StADFFx~<|իWZ۱ fJ=󏾾>">s ?~HS_N_z?RL&k׮A zzzG)*8$TjmmMDbb" j'xO}]hSv/^d\D"rr///t=YXڵk8XP;XNI-^Oӧ7hM1Qq!ZVV8X=@ĨQoO?%''o:.)))k֬|ȝ[vqqApjҒ^5@ːd2L,ˏdFS@WWW,xqe;;3f,\ T*ݽ{={R r-T.:B| +_B| +_B| +_B| +t앙NLzzz=ÊMƍB8|>͛8X8XЄ޿MD@Ϟ=oVVV޺u~H$͚5CoMyyybj}VJ8X̜6mn߾Pwuj allNhh2 h s4 l-Nh45}ttB3f@ @?4Yf4Qmv򊉉y5BUVV{Yf-[TFx%%%2i|I˖-b?N477ѩ.?~,//߯צMHTdΑ8{ͳgy<^waݫO522jҡC/HǏqFK# Ɖ܁jv***7oNߓ޳gz8gg礤$ٳg9rB"Pi?&G`RjD&SwM@ؘ1c.\@mnذ!00u&&&=YYY X2olٲe۶m8"w`3gL4ׯl{mNJKK;VXQ{CJ@  MLWWW,SΝ;v,_H#'r+=vSNiEh,3r°,Jm6P:|>ǝ;wV/HI|HaB|)WEbcR'rc^4LdˆO˖-Án*v-zjsΝk׮mUX@GG) 7/$ÇY -]422D &a4|񅴴^z5C?ܹs=-[|]>(RRF_hJ ׯ bk)++c|7N"y֭[qqW# 'N̘1D Ma4\_@J@ G_HMȍyډۑRSEZv@c1/P%C|piSSS1/ZKhCd  hZ_@J(P M&00pӦMԦݻwYܭZAIIСC΋rөS' +`aVIXvݻgW||Fu, 1pR@/4? " u aG3qi^zݿ ^nu8 1pR@!/Xȭl^ATTԤIp/D%N UUUU|)  HlIV1/k@d`i\JIP_|| MM+@B @cc_?~<^fǎ͈%KhSż膃BB S/8P#֭۵k9nܸgjSX!33'6 !ШݻקO_ -^6OΘ]Yh ,܁jvjD;SjwfPP5{xx!_hT6l6{ꕖWH611)--'\RR#~̋)D"wyHI_3f̅ {Pu /4ǟ;w\vΝ;9JKJJ{4?[*~7]k,4E@5mvPXT|) @@ &W/X&U@dѰ1rF tv -@ў $&rc^Yh|:t(((6Q1 % 1pR@ў ǏϜ9dW"7EW  \Ţa) E(ܽ{E/AÌ3?Ha G{/sss;vȖ/J-[vA}||LFdi:riః t 1ٳgժU8l !x\\\9ȭl^KLLeo" j5ɰZ~=犱c` =zdooC Ha{"8ɊFYhѡCM&rȂA ka) @nɮ:] IDAT t(vHaFmHIV1/ĉ'NADdAq#r}AJ6@c2>nPE Fۊ/-,,{؛ȭYXȝ)c2pRٳgs-uuu31Y) @# $F"wddŋrT̋lD4"wP-e͸Q|X ) @c $F"7]XQ) 999gϞqF#<(RXQ|!00>{PE _h ;w[ٳgzz?c֬Y&79Yv^NkJԚ|;wӇ/mܸqϟA@ C|1={tss۵kvD:uq" lJԯs΍?d{wؑ1^]!A!Μ93ay۷/,,6|TUXY9vڥ >|066n1"w˗/dIIIYYYAHR,@ 6@ٷoߊ+|Ӹq:cր7rnڴM6'ϟ}vtT) Ç (--511AGq oҥ o֭[FFgΜVHÙ[\\Euy4Yt}s玶u7R;@JOOݻ[ݻVPVaժUaaaA̚5ѣ8O4| iii) P~Z٭cƌqvvp?711111SPk׮>kJK.oϋ&444|jVTT<~( " ͛'%%egg+krʽ{rih@ JD58;;=z{ 1S{' '@ P:uNԾ}rd2СC/_\mŋ+K`;e'Nxĉ?!߿oQn,X #y/_m-?>c dMIШa4Đ!C\Rme˖O{ 1{z䉍[_'|mN81m4wBUUU׮]U7_U{ݭ[ v۷T5}  ٓڏȂ(((ڵkeefEEEziHIa4w}?nBA+5##[n8O4ijgϾ_UE!?KÃPRRҥK2mrrrL`)eN>=ydmmmm% y"G }AJ(񪗠СãG%%%VVVV-銬=zD"6o޼DP1/ҷoT BaΝ>}J?~gDSXXتU:yӧOwHt]*U %ho)$33Q3 ϟ"{kݺu5; ۷oժUʅP_Ŀ;l07 2ҥKZ~"v'î]֭[+W  9mqƠ 7^'N3fgT4!% %ܨa4cUZnFF-'_L&[fM5kGDC:99={LE; h+ommf4iRPPІ orrrҞ~Xvm͗/_j~DdA{fee)SPΒdAq<%`ggt4x`SNq5@DDDdq4JNrss/^|@@Cѣc$##֭[kg^Z[YږhiooI/" ZH,nj ?}IUUU.] nݺ1R<8lvQRRBڲ xi.]TUUVڳgukNN|AÆ { Rh8*mZ8E()2* MJ~ G4z<88 ,,,MQXXخ] &!GNمl_A{o>115j;lmm!O=| Yܹs2G3gUZZZ=e˖6ޱczE Bagg3<==߽{`sͤeffVU&U_Rcǎ ,P>7???{gaa!?y^iӷo_͛~2?~\lYttӦgcddyfuJ|rذaʲp#\VZ);6;v jӦ AL&x.S|J̧bL7ϋ/E=W^^N`+[6mXYYYYYK$nLܹ3 wةiCģGG;Ιɂ$!@Dp(*"jZťVQܫ,mZkv.VDԻE-UTȒBPB IH2?qN2gls^? \5\su֬v\fo}TsY߿ɓ^W__X"f%rLrv}JNxwͩ>x9l66m{X,iӦ.4$َ>.jqnmo޼9R0r/Έ#\NT$7PV_<͓;003gT$5XSyGoY}Y(vj)X}\T$Q}\fKz'95_UVVr)3gCܸqcd2_+L)Hvf^I2{Hxii_|u@bj̚5َ>.msa)˳iӦ`M-"% >j6nܸy_{'xBSh׮]ڵKeKW桇R'kQy3g裏T^ VINOW7x$5jԷٳgW_u3vXeI` ;drժU"]{q&IzÎHE+㍐]QPPlI63d]8ۉHl۞ ۺukǨ+;ke]y bt.ڰaC'LeT\vi}\T$9}\l$=kcV<}ַ$4c)jMEEZC2۰#'>>Sear.َ<4ԖeyvMS2&;|a]l4.HH&cK/=cN~gR].~m{嗏7ꘜnk֬ɴ WN>O>y۶mΏΝ;m߶`Ȑ!zkcc$-=H6x&X;̴a.= .ɕر_vr혐6Ypa[T(a?O/_n;N]wKc=,!7hР^{iVFrx]yg̫sx<~Yg9Y_۷Az]_qD6ѵm.6e˖9 YZZJׯw\i}\T$Q}\f$1)--4gҤIgy~e˖-X`ѢEί>: OuLnܹN ;#;ބ;^_:t9眓^XX|}AT${}Wxo߾+cuLټyf;&vLQVV$Nh+LtAr߿t >S9L佱rz/7tT@w/O>6nQO©)Gyoܸw(rp<Zgg'_'zr(/ jX_ #-2R Qq9q#^X(vsP0 DrʰUTT Go,--e4?R!@}\N$zVZ/j$ iڨQz***ˉl@q-eX_/8LОC@ ZǖØL!p* *DLg! ϳ|ۑD"ʉ$Km۶m̢1ak[m!pO6 P *a۹iHEd lo/I#uSklSSxlt9G331lGHM-a}[dUU*++3vj6j(KuuuuwHrH1 Flc6슇 ~}j$Ze9^11lDiiSљZbτx ?>.9>.9wbx;VBm9#I8R.f;"5 .sx?pŝ0Mrq4 D*`u='RÎC@jsڋ#lGNqm.'>.9?%ðCG"QB{avSQQ!o`hȤˉ$ TWWFeSUU*++ ngg Da'kxrN&-AD ÈQyOYӞO  DqT&l]d~z>͞xMm۶+\ʭōǼ*O@N$oy7טZD"Aq"+ 2QkYQ[zH/2/f4MxzR9$R]]-ȉ$flGNŗ(-X8y Y =űDΉf/ tϊ`$J,GlKtq"°Ԥ27 s/ZM Z!Il& VO v[=R>.*>.9rZN0%$b1Î}\>V>. .cA+~X7Y|WFqBrÎI8AHqHnw Rؤ xD >SrO"S|c&h< a_)NQQeH$(NEqۺuʗ߿96-GTgxDbcNP8ʼn8X_0H$&T(NhHrH*_8)Ұ{vD 8EDl'X_0G/wT~bvBHԤN]$EE%GsOaBnġnYr.9Gt])QeqGji=ПlueJݯU6Hrx*@': N-oe8*[*bq#' 9_Asu /v(q )~ &861͘IZdjɖaG&9{$ ܭIN$} q00Ͻ1RlOUSSc)*)))Zz D%GCN1 SX_xx$#*g;nRlǼ8 }S s/ ߜ伍*i"du Aqг(Ł=/RZZʽ " %h*PN IDATmm-E:f;vLr)(*MRr0,Cq` )|@$ N+=EEE5/QSsQQQ 'HP@԰BAAAr!;a+qQo$7|%Θӓ]DvLCB*0FNoF8"Zˍ5*ǫe_OeeNj7t9d^f;%G\hn  @cfpG6d:^!p*r RN}̋2/.=Jx38##yDm۶mrZf}ϝП*hFˉ$| .Egj!PeURR"d\z!7BKw{*}\T$9}\r$*Z_(++lQ}<=MDb}AHq4f;~}}!:S >K쓳y$;X_ps_V&Q=Qq_>.* eCǭ~%.0Egؑ)o葊$َvOA[Ϧf555BXm3޲]6YDFEN8uHVx]]xvLn8V/ q9}њ 4/h91H>d ;.2 -p_ fbX_wyG@,*y+W*{rCql4ߗ6YK$Qy+]M[ZZ֬YH$'+@1 h˖-_|EdPVıQ<[;::zK.;̧P6$L $??NfCNy~>Ѽ<;L|x<纮s1_w9d#-˻o/0 ;c/Ot/Yzy2+Vy䑯ZAA'/E}?g̘.8#?|ea$^ |O<.//O# ,H$̃>SOu_b<瞻u^H&"E!P<7|ҥK0曯ݻw=ӫmUَWMr/d9~?/Na'? 9䐧~=yRavmdGG 7p5״q/\%'<\,{WJ}߹sinMӮ;S ,H&y?|jy)SW4o>}HrpTx<>}+WjzuuG`xc,_' aCdynڴW_ꪫfϞ=}I2?.ݻw;&yJJJ\)N{{SO=ui/)**:s444g?;ӕtI"01L&_|)SDb}ꪫo޿w| H`q|drN&=^:u 7P\\e˖nl޼y#GCHH 11ts9ӦM[hoq=h6a„{lr8tvv>\pŋ/^iڈ#xaÆYEEE?%I"01׷oEM2宻Jdʔ)skwb9%X̯YU"dRVVpO? K.dٚ;gZ_1<*CqLL8OE]tꩧ~r-̜9ӗwsiy(+ʸ>"d2O?^Ӵǿ{キ'??ѢEC4NzMSI"01WRRhѢ~io}~~Î ,H&#G>쳩5f͚5go" JqZ4m޼y3f#<e xgڌb_~M7Z?ĉS***8qE1 C^^^, cٳgˏ>aR4hyFNy(]v==\q?ϟz߿8Ç+vH`qE0|嗋/b .駟~ ѣKKKn" Pq6lذlٲx<}RǏ=^{;3gN>O81HD'`Klٲ%~>.\---K !{┗wIz=OfLqx>}.]{vk̘1 KԽK(\crݿEx Y]|'չz}HG{{azݮv ݚ˻|WzqԽ )NӧwOiRֆ&Îy(Nǥ'|>?ttttMi?4{)0ny$*#9UyDEJ%I"4$uݯ%0< ciLvƣa{0i9ΏgR(K"9̖5$iM3L{{].HBSO۹sgםR7,9=E׿q֮wy^{ʞ/* VQofwNKrzWH&͛7?q}}}ggg׻U衇F%)Eqcׯկ~nݺM6-H 1 SO=`?N8BFX{ŋ3= ڨA' ;S׮]_}UZMyVq5ɽ>c/}\TSO=uƍE.H6*w )[wq&RڵkaGZ˱W"zGN${Iҳ\\+ǻNUUՔ)SÎ<6xy}Wa}!swqKgai'*;yNjG]o߾/>=#-I<lU00Obc0eGx y<"[7n:tC=4nܸ´$yyyiOWZ$yDH]gyu>ȑ#FqdZy4HbHZwWzݯlGyivG̙3gذaX,Gٰc/>+qk}vqX_0B~2mڴ3<3uڕ9Q#'L}{{l'&Qy(N򈊴aMn|&iؑvl(I(Nz$V'^Ql;i 2w`e˖w{)ŋd}<ӫÇO侾J'X,kD8#*R>}ǗH& JDL_azc&8qbk<2s= ]۷oOg_L&Oo#<;+s‚ ^z?tҥK_ ../O?+W^yw]L4)k>uV re4Mb~qX9 ar.HPq9L=2%)aӗe:QS}xq1IMF%2I/lq_KَdjA78åL_0鍆a\ve]뺞W\!b#8NS'mqxQũ[n~iqZ/y2MG(y3>r"$Yn~Tyӧ=ZeqD󔕕Lc0uuu*#5ʴ$xfJoݓbn8pDTL}ܯdK+Eŋ^S:::~q׋ӗ<(L.Yv<-/Nqa׮]?M8j(E8#*Җ-[ӫ)S(2Ræ쳴f\y^|DIrdf͚ŋmvJɓ=Xz+?<Ϛ5+{o?0sʔ)Y״ܺ[y566 S\\l^oeםuY-\0k+))s5MK=>.6wyl/,HpرƝ;*t}y:촵]pmmmjxFRDlR8< f;zW_};/󩅥06lH$k,iOKr9眓?OnK MӶm&dOQyu}ݺum߾]BYD8#*a|i*#0< :ZrbիWk}t+@DDZe'//Zcϫ '$Vܕ<.,5l2&u7N8abq\#>a-N8Q^^M7ݔ֢ SRRb'χP`nF098LUP`$i!O&d;2=@U8تAu\ ;=Bq>~ݷ>2 cܸqʼn:::{oFT<"Qo-Dh޼y@'|abX0w.**"adFGoŊXSO---5 {޷␇fƭZUU{L:5uGO]9!*LoĢ IDATiڥ^:gΜL۪9+!l9SVVdz*ɬY.^YCqGT$ӥB~ H}1裏N<5(SZZˉ@ƐCqCq~Ǯ[n̈́ v2Ν;7lе3~_wސ!Cz߾} a[& 9~֭[7nܘz³:{?S6' A|Cqr#'RZP#_n; [lI>nܸ8뮛1cG=h ]׋]I~0i| cr\d ;>o߾=B'tM6s̃>xweטq uvU ޒEGO?%r_|%KN;믿~}Mu>Ŋ }j{(`}?z'|2mڴ,\p…={}wysu+wx^7rg&Ma=O"FZqҚȉDqz2n{ ]#O>6=C= Ja|f#Y,Wyyy՚ag?[hQc~?܋2G1wq!"!OَHӽKkr7O& !atttMGo{{"GNCx]vTws=>LP`+ѣ3Ivi_FTe<[N2w}$]_Vv٘<Ǖ<rȀzM]K{} ȭ/0z_Ce.LBi͓USS0pQGs1$u=`ni_'+X]c+ւc͓cɼ~r#y˙uYgs99e*n~faiÎqa$]׭h_d2~+t1I.|q8&LűzL8Ryl|~1KGG3 Scǽpj>[_0!z-3GTqSWW2ɨQ,=>m}AaҲ*;L+_}\N$9}\r$X(8*TVVknuqJ2yqV)+Y&XQQ=y)w8P1)'.ιO cs*wUOp3= 쿐nӦMK,۷Yg[fssᄏ{va SEFq5Qjʕ dĉ f P$<޺u?^_vG2 V$9y8XՍ7n}1k׮mhh>|e!1aՠ۶m;C+**zM{577;v,b}!͝;w7xcya>};w[im\#*G+/vO4u099*?|7gZȈb_~i֭biuny\,ӛJ^^u]iŋ~zksGT][[[%$1 Z?1]u09T7|sȐ!&kok[oի|7/t';;HFr=;Q___vatP-[֯_oq8 0Îc=f#'{v:4c8㌑#G\-\__p';zq;>q>S|W5Mꪫ2d29cƌW^y%//O2/}|m4mƍ#'$^*0544hv,&ɉ'j_x݆H'XyRk]GGرcl,k"9{(iM.looO 8^W<'yjkk8qΝ;sY BiƍOj/5o L$ #,ؑwCC ۮq$4i+OAAAL]S1羊<*#87o6y_}fc;{;xLNI0_u]l'dX_&Lزe˒%K25X,??8g[\|#38≦+W\vm$Ʌ j9]قUQ(N9RӴ YvK/i6n8aGZ}DVQR?o ƍ& lGZ}q} Sq]YO=ɲ|PSS3f C{{{K/t޼yyyyX,+7s=O M( V{ϯT͛7oذ!mؐ!CΝC`EebݿoNZUUu%۷|dr---3;Ppw^PpI&;3/_~gذanmm/׭[biɗ^za'dX_H_}ݷ~O?O?N;[o{]y9z[$s/yu֭[.n e$&5i%ӧϒ%K͛CرcիWm^^W\1cƌ>}FqD"6EUQyUFTA-[7o;vZjժU]ׯߵ^;}tep#Ϙ1cy|֭[nvt̙7Y\^tttLf̘#ʺb $tvvӦM[n]mmmUUUKKÇ>vؾ}*.ytqe]z֭1bİaƍ)TTTUQyUFTok]vmMMͧ~WQQ1lذ88ȓH$̙smUUU_~{#{ & ޽cssky09rȑ#O8 !O"Q`u=Ô)S\2.<Ld<?~{J}YCq‘O>~,.8{n*4nE/)ܐD}9enzNYAAŻC z+[H@vLCR) JWNo,--37SDb}' WEEENjG񪨨.'և"Pz|ܟ0,sBrh"Sl>1 z.Bvb\NZsK>wUUU*_Τ>O4\ʺ:yW6HrHޱz͚PÎt]x䴜Hg;Ul'}W^@/ںą~ب2IqqG\JqHrHޱ箨EY ;^uS"KIl'RW3۱{oq"5S/ ,e,0j,\we }ITI0aGf!I cGuf;N-TRb}Q[[k)ѹFUׇ'4t]8eFMMDj{wHEU^^VVoq혠8.'X_@, M tcV}x Q<+. ;G="a $ 5 &lܕ ` p 9v̋T9XX_!_"E8\-'LR@԰3@,`尾 S/s_*D"m+HO=kD ;>a'SR}?pFƅr"1贈Ekx1QPXXh ;l'k}(>.гTT_FeeKSS?ꢦ*_lb##yDu׬) ONKwS6vT9_uiIxlGNZL/Ǘ$5=COUҳXX_T&1&c㩬4mUU$eee6{f;rCq\,Np}qQ0 `sB߷ # b}#}n ;cr!,­DS#ˉDqFg$RÎ@ ;$| ;6c2D㶶Zz &8>:HLH]`a D$m;a>r?EMӊ㒱@6wŠY1|i/&X$/0crO ĠIqx"d°cBu x`}`pLJ[){-plD RRRB w/va}A/列%H[DAA VINEYf%kllTZV"tvvJ;vr"M0x(B&g; (NEE6;nH'֭[U\pJKK}l<6vSR(9IF 8E8Bp}w1CC{En}A9w."0>QDxv(s|>}(lGrqY>7ΨDuu?KTUU%8_"ˉ$K׬_CgjG9krDN$oUlǜO4':S 2C>Q Vʊ *}<>.9w=D_O*7/vzrRa@^+9v"u =b}5nrէf,l"#!1[3$fq~*(Ԥ2g2Ȋ?{c)NMMէDj} qɑ(NOaZzJt ð6-'R/TVVXf;űx{ϊ9l* 0,sBrh9/@X - X_"D YQr0,#W pBO Q(ưxَ Ra}8ZZZT`@ 8 $ @ۍ"" / FwsSR6H$(NEq~͚acjG Nzʼn,X_HTR0q9nAoF/W\\ΰ #ѳ(P}<">8iN/EE⦦&/WRRb. " U.qNʑ䷊RfKW.*SԤJJJ,=^W9=1W; lšgG 4@dW\n57߹fLq$G#5Xm g(i Y# DhUJ ɺ:!~!R oGl$9}\r$`)B#jjj,=^.'>+*g~ ]t2q ̈́/.xQ0@#;Xݼ04Q v:싺uKK>.'>+i9E9 BE!\~GYKWEyV6St&ˉDq =RV wBD`$}\l혷aQyΔ3M|/Nf3]X9_x.qDb}!p 2 ۨwIl4DvFhv(EV/ rخ`L搅ОsmAb}AZ@@ HyyWA=γ2Gq(ưx3;L0P0a}A_ ̱ v ,C̓)p/I˕PsQQQvb}O! px( Qu]i[*a1L2M呹4 Gx<-'÷i+q֌nbr>.->.*e_ߘ,j۹ĖLlDŽO4dd"pg UV;@QQQQQa;wtg͚5gyfW1a_<]tQ~Yk֬߶噢;l9~[Fuy}_=/[wRr؆5M;Vy뭷ǥEDžDJ<~nurO/+իm7>sKQQQ"Wv nKNdٱcǂ ]9^xgc/{X*B7t]םɢ")>ݻ]i6XlǎN01!m3ϼn7:=gWWWo۶*s yA}lmm}駳-*RO4>je˖׿WWWg=pi/j+2ǫR7m#kel7M&O|'g6lpillܺukqqI'ciiiy饗mzIill1bW_7 HrHu.g׮]/םkƌYٴiӏ$6`p {o]ӂ2xڒGꪫ K}瞚W2<{챬%+t}CO&`_ - `Fixed integer overflow and EXT size problem `_ - `Fixed array and map size overflow `_ Using OpenSSL ============= Borg uses the OpenSSL library for most cryptography (see `Implementations used`_ above). OpenSSL is bundled with static releases, thus the bundled copy is not updated with system updates. OpenSSL is a large and complex piece of software and has had its share of vulnerabilities, however, it is important to note that Borg links against ``libcrypto`` **not** ``libssl``. libcrypto is the low-level cryptography part of OpenSSL, while libssl implements TLS and related protocols. The latter is not used by Borg (cf. `Remote RPC protocol security`_, Borg itself does not implement any network access) and historically contained most vulnerabilities, especially critical ones. The static binaries released by the project contain neither libssl nor the Python ssl/_ssl modules. Compression and Encryption ========================== Combining encryption with compression can be insecure in some contexts (e.g. online protocols). There was some discussion about this in :issue:`1040` and for Borg some developers concluded this is no problem at all, some concluded this is hard and extremely slow to exploit and thus no problem in practice. No matter what, there is always the option not to use compression if you are worried about this. Fingerprinting ============== Stored chunk sizes ------------------ A borg repository does not hide the size of the chunks it stores (size information is needed to operate the repository). The chunks stored in the repo are the (compressed, encrypted and authenticated) output of the chunker. The sizes of these stored chunks are influenced by the compression, encryption and authentication. buzhash chunker ~~~~~~~~~~~~~~~ The buzhash chunker chunks according to the input data, the chunker's parameters and the secret chunker seed (which all influence the chunk boundary positions). Small files below some specific threshold (default: 512 KiB) result in only one chunk (identical content / size as the original file), bigger files result in multiple chunks. fixed chunker ~~~~~~~~~~~~~ This chunker yields fixed sized chunks, with optional support of a differently sized header chunk. The last chunk is not required to have the full block size and is determined by the input file size. Within our attack model, an attacker possessing a specific set of files which he assumes that the victim also possesses (and backups into the repository) could try a brute force fingerprinting attack based on the chunk sizes in the repository to prove his assumption. To make this more difficult, borg has an ``obfuscate`` pseudo compressor, that will take the output of the normal compression step and tries to obfuscate the size of that output. Of course, it can only **add** to the size, not reduce it. Thus, the optional usage of this mechanism comes at a cost: it will make your repository larger (ranging from a few percent larger [cheap] to ridiculously larger [expensive], depending on the algorithm/params you wisely choose). The output of the compressed-size obfuscation step will then be encrypted and authenticated, as usual. Of course, using that obfuscation would not make any sense without encryption. Thus, the additional data added by the obfuscator are just 0x00 bytes, which is good enough because after encryption it will look like random anyway. To summarize, this is making size-based fingerprinting difficult: - user-selectable chunker algorithm (and parametrization) - for the buzhash chunker: secret, random per-repo chunker seed - user-selectable compression algorithm (and level) - optional ``obfuscate`` pseudo compressor with different choices of algorithm and parameters Secret key usage against fingerprinting --------------------------------------- Borg uses the borg key also for chunking and chunk ID generation to protect against fingerprinting. As usual for borg's attack model, the attacker is assumed to have access to a borg repository. The borg key includes a secret random chunk_seed which (together with the chunking algorithm) determines the cutting places and thereby the length of the chunks cut. Because the attacker trying a chunk length fingerprinting attack would use a different chunker secret than the borg setup being attacked, they would not be able to determine the set of chunk lengths for a known set of files. The borg key also includes a secret random id_key. The chunk ID generation is not just using a simple cryptographic hash like sha256 (because that would be insecure as an attacker could see the hashes of small files that result only in 1 chunk in the repository). Instead, borg uses keyed hash (a MAC, e.g. HMAC-SHA256) to compute the chunk ID from the content and the secret id_key. Thus, an attacker can't compute the same chunk IDs for a known set of small files to determine whether these are stored in the attacked repository. Stored chunk proximity ---------------------- Borg does not try to obfuscate order / proximity of files it discovers by recursing through the filesystem. For performance reasons, we sort directory contents in file inode order (not in file name alphabetical order), so order fingerprinting is not useful for an attacker. But, when new files are close to each other (when looking at recursion / scanning order), the resulting chunks will be also stored close to each other in the resulting repository segment file(s). This might leak additional information for the chunk size fingerprinting attack (see above). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals/structure.odg0000644000076500000240000003643514716225054020217 0ustar00twstaffPKyU.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKyUConfigurations2/accelerator/PKyUConfigurations2/images/Bitmaps/PKyUConfigurations2/toolpanel/PKyUConfigurations2/progressbar/PKyUConfigurations2/statusbar/PKyUConfigurations2/toolbar/PKyUConfigurations2/floater/PKyUConfigurations2/popupmenu/PKyUConfigurations2/menubar/PKyU styles.xml\rۺSptIǶ8gN9?3qN'N:ӫ3 I)Be߯O]H'u}f#baw, ~yw)˯Y8h$͗WO/?+X1%,ެi.RZW3ƻ!y] ]2}U,|$+@AKxA[.($'iA&? %ܶ6-U$H˟Zy 7 ?;)$_0K4g<0MoEF|s{1.&cG|͍SwY20bO̍77Fc?yaon?im5>'t2m0x> )hY7XMf-;Z~[YKB$+\~KkO4 {7 (TS-#l\ /V`)8Nʕ+-|F?y7z 2y6(#0g4_;< ! t"de6"{4^/\@Hi)xD<>c"c/w/l |VS|\)UN`$ k>VI+Ɋ]MJٰv|a9%@ CE/}LCr_k ~'XRPY+\,8_(qGneGݔˆl5㊩ Ǹpl8^_>Bed M:AC 'SA=>H^U (0(r=}Y~@,dq& CZ*Ԓ~{?fQbT%'#I% eiRҔTۦHzC0GKVWvMmqKƯk <AZH䏦`DPxamb<]|ޔ"]>FsK[t5j`*Ke5B>@5*)T״9i/[KR"F8,W3b)s_PWJo\w\1tír L3W%'0#n}J}8H步~(0ݔ/kEUǙMP#nIlޗV=[X,Ht]Ƚx4K]51 Qe H=?XlS܁š)Nwb)tT-J[ߥ~XCw_a0oex9]N_hSpTw4aOTl(Z֮)RӾ-k55Aipwo2Y/"uo 1A}!J ,,ߓÃlDGB?u7vKݸpiІcEt6śh2\^\N <Ї,aG%yi5ޝ>)SCwJ{5nEߴ5{05'9˒hW5t=,zwP)k/5P+f[n]j4#+FG`+vRxfTOHGч*_a~~<̰ U!%3-Q7JGUey9p-TLy~~$w[0G_A'O v vwg;̬zN}lzXmW{u>d[Bnm :ǩ3o~v7?#xoF%A5k~x/t|#Ӭ{n*S78vs=&^<0O?N<)cؑ8 9uRD肏,0n4I|m}lV6AišBFC@0 uBƉ~/e^ۇGݽ1W8:8֔AU!صCEkV=©rʅ:߻2h- PKr_C% EPKyU content.xml\ۮ۸}W%K'h2-0@9KAK$$}c_RX-ى}9σ-{{/n.^}^d$(Ɇ&y<œ_?$ T PNٷĴs2-sB3yAi14TLU5S}|+'L^|I4$SLj]t' (p* ,IeYJ]L!aSk hzVז')nVK8 C -KP,hvP}_hpiڢr n H1&Ũ h!ɀ+PZt$& aZKA#\,q*@)P`0=p'xVɱB'`}j)\:Sչl[`X[ @4m Ov ZV Z<ZX_J6$U"4Єe8|;u O+D ވB/9'͘g?gzfQY$" ezڴS! qZ>E`t:͏A $ WO`HHvtb@޳L]{95: pRLt1^ lYkMu,הIF@)yxUv&Y*߹3{Rz9ޣ3jY6߀?TYjBs17}d)1Y3Pq) emw ¤A{rQڵ8+LcEF'aD8bU1.b s z1ҏ? S \Ś`8d[6DU:XR/"iD|vl 8*[xO5ƠX$A\̗nE)d=8r+`# 4D'SgÏ%I{+IJv`%Qb&Rr#4͒Ie4oYNB>..Ko`az2N9~+gr:mq TTGu,:ZO)b>UOYl-0]/&Q2-%!O.@7q K#Jtt?'N/u7Yb`Sҁ 1d__4sн(Iә>Ihzԫ5EK_T݉DOKɨ50- PLN<4jyu#W)C H}ܛv75ɣu39Ԥ@L{OC`}{sӭlڸ7&cs&>ݝCn pӪ͗i TftrZf)c&?nBUHvûK ,N=F`څ~~d6oI;A!Y@E"I~fnڎ>F}vUobIȌZu;'{ZV6PK 8HPKyU settings.xmlZS"9~šګ}@D=JԢr{fșI0*3f>܋L}NOfN??l7^<fp?TNϭNxLChD"Lcຢ@kvp:Wm!D3j4U_\rR۫W߃l4n AU NbP 6gXĆpqQŔm `VB'2b HP8o ]g/mjXְRxxiſ .f&7)^'țoT kK C7%|wb·O3`4y'f{p.+9`SU*Q%ksABkǎ{ ^l{9B\\lu)h#Ѭ`*ry% |_~UVKnLaSBtb![sn6 Lz0!܁,r]t;(ЛwmRiڟiߴ 3qCxw‡#ŖdjJ$Tpه))34&2Ubf*IPsNi Ѥ*9rFG>]DMmHhI45=^#ڊ>%MtG)IK؆'DkKdE , umZ[!G4<=,oҜ/{6 uL(7CgK_ }$E0oo3WDU6~m<\մ΄xx'8#჏2ڟ"JNӆ=faao|a_? MD-ݸ%Ht$( <J%7L;;le$ꋘR>ʔ$tƱk#ϔ0W^iaAvd6.#s,K2KQ< OO)tٚA/>)F 'y#{% #% B+4_=wnIǰ !`ŷH.v~aϧݝj]PKFm|p&PKyU<"55Thumbnails/thumbnail.pngPNG  IHDR<fPLTE """+++111;;;CCCKKKSSS[[[ccckkksss}}}+5_IDATx흉b:E`Ș U[4 >'QmZ@ @ @ @ @~3j[jn<ږ෰* ^hoDQQd$v{jn;fqwoVpa8,B@Slp8 dasUi>}'g1 "QH!5:t/=aez/ڷWk?S (*[R5WGr$pif*a +_~LyfÞdP9/"~u^N931'Vd/BU) iZLfuּY Y…a{9W)39^ %Q=X`K{J{ڤf,:ImbcKs}t_@<{+9d{əeuo$4TL=Z}igpAF;#Hnɮvg/!`n]˞r6~p{>X n??}i+v kw9OdW}SX=><.y[ty =={<}xr>Bc`^oX4 |v7Q"@B!@B!8B@!  ݒA8XH 4ߎ(N (1ᆓB"7P(r!S*!`9B(!`㣀u.YÎZ,<1/.j|M,}hp( Q7КDq,ٙIm,Zn&s+KuIb.em sJfakկɭ/űj*F=;\X5Ku0OuJL%cY!*Z,u=/I=e:'%& |ߐKC @h}y, ұDžTd'3R.JƲ(2*a?Z%b[{cMeSLrjSJ-M +Sq4ZZHڇP2B?>/nXp[c"4= ,M(%tp.1t(`8d"t $1!~A~f~Ol`U@w\Mu ySBVtgt sKQ߅Pį̭+ݯL_+ wc"!2:|!;V+ˌm t]>,[^ҷ(S =a pne]=S_xʱ*|w'MK@󹠃;yEYZ}L5mM21Tk!ouph&;K~/ߕs/2 *3DžN}t:K:ЎХ]LuúB`|Nr  w R/@EOI .mW7!KUyI.\emյY`r_9@ K0uO&&t3j!@lq*R*\l`GY(IӔԜmTؚPc_F?V,6YZʴ.=hSRظ4 cCz$p'L [p{8Dj <0j[@!ljC)ab$* I׈$>14MTI;pp:lлᕚyZUaoBgpZC)^?럼s}0CXd"w$pۧm\|=EB|.^BAZh&p#n=9,ܗN DZ;L u3FKGHVZ3)\[i 1[Qe˻[B@s}0MjeN+_?܊ҊNU_ A8=s&%cZ+`…/#!` +=~w^GAKˀpy\ZEj*mo׸(Aji'?X7CQwz?s,7BW~0 ctW `٣`"hDnG!38-|!ےC !B !B@! #|P8_1ssa8:S:?pcgWPnԅ5B[bȦ V[s^*z!`k 00̠d(1Q߽еY龗3 ȅ[AiZUqYN=XTuLeٜp̖$Tʲ<>D~>(ZVQZRؤEVƾ9U{/d_nN]'账vG֒Bz/"Pf`IE YXE<w ڇgܾn^I45$VњТڇ@(1ED@hP<_hX<\gĿdmJ١j1SPM0ot=UF׏D5 BkO4H#Xͱ7rA_< CU9@`5]nd3j.DuGO4u6 zs˲KA7KTtpEF/qGqlB|ds`̼Rt`cІG h 8`q98B9EO%By.Գ`EX}۵vt'`a;S/v(GXV@G# @7N e6 rP.v 5 ش4:MCx8bp]"WUąk4W/֕ԇ@sjU#Z kg>^;+&y{ X@xeV :t})zoNװYF8֥$zWh?w!h~]+NVYP`/mZlHTf-R:dK;u:M(k)/}Ji\4aiτms`u.IZv0R4]ID.gIR*65 X)@vIGZ?HUǜeSq8|?RwB]bE_3~fv2$UAɂ5<%ڕDjujZw#/UϹU|Ncx~K* /;--EXH4:Od! ,|uJ”"2w9'0_CØ51+˶P(&3HщD4-=>TaK|LZI`Ը:WAFV9ӌ~2>`9̪TFf3,KvB|,D̶TQ 5g˅Td 4֚d^ @cK("M*Ҋl0RQ6P4)=O[ ̒PTq25i {'6Izh8sMB! r>7nr@JfSDv_N ڦ"}epyȽ+I5P)- D[W3;@sfgu#[Tj0<;2a"oyKŢ;0v7v^P0H_?oU=>ĭDL7.gx`jwwv?@p{K%7@ஆIwE!zP\`r> |G?(n\^g%bF.]J~& m0ϟ:8A2~ܔ\9 tOLp8/ w W_ }H&!k~&`'B7 !eUy l+ n468F OGKݘ \ḨHqdE Kq0}wWׄO?ߨn L8r \.VY%sXe)\H8 9Mw(unQߜ#?ѯ?@7Ss|`9 '1%`l߿ļ4Hp^ /MX(8]Z*-Co0EЊ25YU P0Z[49:-:*ˑ,~k@8RIv 2xs9B9ÿH@{gq,`#S?s0 FK[>X89şvULbkVuX_0˼R9G~lBvA=G%S~ÏC񜀶'kG ˹Z+k(,y(*F@y(Hʦ3D!9K<..e7qG3ҦEY٦_a75eUG4cnPÁ!Hk&hvB.97TVKf[Oǥ\C:I-!jX+1JYCWˬ>YG6oU_mڤF{YeXh@|dQ: <|}pEeBweH h9!]@뮴4]#kp銐%0kVuO6"Rf}Ӄ>yD V0 R,]P8!d pU'98@9 @؞Qnڗu&& vmmilXt[ɖg.uЦ @ @ @ @ @ - XuS؈IENDB`PKyUmeta.xml͎0} Dg 6#uTԌ]D;-6CifUϱoxjJ(ľ).d,?E0\il卨4`Pe]^K*# U ZFUr5C]9>QJv 5"h_Zsޜ0CjcT#^*x0|\JN'U&iEs/;ine#wn!h` S#[Ey#wzZ~w !&48$g1dK4&3M t(874I)IqnHV\.WmO})qubXީ=/X<W_x罴+ c5'q9a&!yx?~d&.N)!ç^4< KW֪PK:չ(PKyUMETA-INF/manifest.xmlMj09R6EɢP3 ~s&N\J 4{ov?:AaU@ڵH]þ_6^I·ܣx-I"FI(N(ɿrRV 삶:9ԪT/B0x8 Iin+Pp0ګtlt0E='j_λudbOG&t|L3r$l!;D+*lqD͍Ry7|8(|䞺;"ث_CRZߊ_q PK;f1%PKyU.++mimetypePKyUQConfigurations2/accelerator/PKyUConfigurations2/images/Bitmaps/PKyUConfigurations2/toolpanel/PKyUConfigurations2/progressbar/PKyU:Configurations2/statusbar/PKyUrConfigurations2/toolbar/PKyUConfigurations2/floater/PKyUConfigurations2/popupmenu/PKyUConfigurations2/menubar/PKyUr_C% E Lstyles.xmlPKyU 8H content.xmlPKyUFm|p& psettings.xmlPKyU<"55Thumbnails/thumbnail.pngPKyU:չ(5meta.xmlPKyU;f1%t7META-INF/manifest.xmlPK+8././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals/structure.png0000644000076500000240000045616514716225054020240 0ustar00twstaffPNG  IHDR ㈁\39|<1gΜuf3i8IZ;q(|q(|q(|q(|q(|q(|q(|'/..Rq(|(|PBɓ*G^?wʚ5k*)ծ]I."O^EP=3Wvml2u52 4P=jԨQeӽzR;vL2ߨlٲIdׯ:t׶ .TŊ3}?~\ߪdɒݻW-Z4Y?XV# >9 }٢,3|%K${\_m\!I< >JQ |v^~emی6mTe@ت+W|&;ף>f͚m)z N6_H.ڽ{Zfiq%ki?d9R/9~x~zբE :͊ԢEkI{Æ _L\ʕՊ+uzG\^OR> lS?:)BҧO^1q%uyNɓ8Ϭwѩk&/W23=zHQ1f̘d}U^C8a/|{HJN->p ǡp ǡp ǡp ǡp  kHa5D žv7#H;v,'@ q(|q(|'j ĉouSQ?\5m4Em=&j ?<#ܻwrʘ1c?ʇzHr$[bz܏4hvޭ 5j2gά/ˏj;C6l=cA2&裏j&սޛ}W'TcފqeUVj5k.7+j?ocҥ۶mޞ{~Q6 GBՅOի'CL3.?|Z {1\o߾]*UgQe3dȠvnX纥\pA+|R|,SL ,z&]{[%Ku̙خY{GX Eu ʂ LR\9˜`i )f$5>؞g<ө$-oخ]d4 ӧO-~[ D ԦM9-Bk!IO.:Fotw]ElI&՞ٲަ緍` ^|a^"q(|q^-[;fvauC^d'Y@ k$bc|q(|q(|q(|xQd.%ؾ}{)V׫WF6@sȑ#{-I{h)+/\Oȡܙ3gg˖lt]Hڵܹs|;vkw@ ܟ;wc.eF믟ؽ{]ŋ%c"=ɞ=}>:e_~)iӦ m۶Dc8TXqKE_˸ oW6gիЬ-}Y)r]L_>䓶2u ^P/Zh1]-2͚5駟)J%Kr]D˖-y=z]9{F˗/Qf=_fo>3f4&YQ]wݵ[uNL.z>FvkԨI|rM Hڵ) _>hGl`O0;x{PB^[O~Ԉ#Tjԕ+W?pCh(|4f|w_|UV%_ى'TΜ9=W8/̤P/^\ڵKuj刂$ˬX"Y:+V>&={V5iD[N}7ꫯRSLRСCU~Rm;E=gիWwOLlܸQ'{1"ҳ1P8P8N !#gp"p[`_$>C8>C8(ƩȠEL*@ΫW\9 -f PVǏW#G0!EYAڳg*VXwyy-zbZᓢ 4L|B^ƍ,/ӗ˟?UӦM؞c 81;Ѩ5kVeK*o-Eq$9@`(|QXDP8p>>'?ܡC lԄ :XĆ'>|c|q(|q(|q(|q(|'/...19>z]_׮]UΝݬr yζG q(|q(|q(|(yJo^M81_vMǧ@PlN^iXb*o޼w/;n8թSd޽=zt\Z5jժl2椨.\}{ͪhѢɖ3=aw6I/z9rhyLV\lrʩ-[ؚ /|`uY>ؿ{_~9mo; Y3K{bӦMYJ ǏhuY>Dh::Cq(|#GZD0ȗ/޽{au?Y>=3bĈ׬ "#8Rdhb33PbGs 4>C8>C8>Cn`xիVox#Ο?}vYfK'Q3Pz ǡp R$K,‡z__Md\rl٢(OR&n߾GC@tI/z)XCC@ƌ3N-b)BD,p ǡp ǡp _}CK.@ / / `"Pz{d˖ŋ3]z5Uv55N> coZzE= eh}w+g\׽O?tYΞ=M>uԍy=r,~yo;U~lm͛71v.cy'`FYVZmsQ/^z4ޞ?~رcGɬY@V9x)}:mڴW =ە=}Gɻk񶾋ϤIڙ-vڪ=^9NLhb\eyn+MF/7?r3o,XP 8/Fp'Rp ǡp ǡp fU5j~d+T:p.f̘-B .ҤIݫϟ?Zb{!Cɓ' yl߾}U۶mĨKY;d|fc6l̟6mVDL>a,vqqqZzڿ߿VEO+G\)&*zÇ$>#f ^ҥK'hfΜ龿f͚jӦMoF^64Yx/|R\)oop[? X1Y歐1fJ*]Ν;2v v"EO6=4{1YU@gp 0 H(|1YEs _ 1u<;P8P8P8/p$ARK 9/Fp3pJ ʕ+?ʙ30u Q<+J#P8P׏0vE჻5nXm޼Y^jžWm۶̙3')IQd (|ЊQ޽E8_^^|j֬Y 8> .7xC}O>ђQ-LsNI;yj֬Ye~ݷG9YժUsOlR_>c3dȠ}<9rP-J̊+ Xy lq(|q(|#Rz%/pz$ (|ڵk9Xxq)|9n36nxKcv%v0.swrLo~K{v2et%~[/M4¥I,Yλd{DqƊYf=w]w֭;ov4i\7k,ӧ%Lo޼|bdΜBS^gbi),bN2RJ.ͥtBBBW)z 2@5E5+zXtc޽{oWbō/Y|U||)v.> )={5r^}ߟ2eJ+=[v%Kp) 7x>UI{g?7oޣ>wa\::1?Hӭ_<ȩOEXP[vZjH;Ǐ(}%]׮]/+\ﷇH{ _NCA$<p ǡp ǡp ǡp ǡp ǡp ǡ'=-[L;v,g,x^mۦ]XDs>4e1>C8>C8>C8>C8>C8>C8>C8>C8>61U=B(=B(=B(=B(=B(=B(=B(=֢`ĸP+ݔ<#InPB)z |\ҥKwE)|GGC4G<4jhK#ȠF2~ rbv՘1c,÷~y*#_fm|ѻ;OOM7V7o8nѢ=o}E?T?iR0aVx<2Ə|j۶ulܸQUX1ɼ]vӧOkM4R' _BBzGɓYfZ1bM6 *hӋ-ڷǫkIڼtʘ1zG}d{L-]kfyjƌ^D/zK,qO{Kiǿsd ^ &GO^F"EԾ}O6+jz3._hSʕm,xbѦ0Nlt[XӃ| DڴiUYA8 #(zQ(z%E/H k<o8 EP8 EP8 EP8 EP8 ECɰA"aOz9VDo8 EP8 EDmѓ_}%u9PqƩN:k׮n!G{×^Bǫ~X~#`.]kռys^(P@:tHΝ[eȐA-[V*ss6fϞzM>|XtM2Fz{9b g͚5*O<ڴc=:t蠖.]ZjԨ̙J.dE@gեKxbmޣ>,׽{wmhѢJ*uyFm֞A)Dm3g,YR6=)B5kԖѯ2ݻz饗|<#Y?b„ v۵۴@}^nݴoOm/߁ky'(xJ=GpG {۷o {ѓ]C ZqGю1=B(=B(=ĢE,?`G쉢x2eJ+)VA<<3S)zE(a-zqqqlF8,쇤~z@r9r8z䱥JRPB_XO=~ V["سgO1+@zRʺ~>Y^v{ѿ֎v…:XbEu}q֭Rjյڃ$=EU/x<}XtR\Vxc6O}?P/H7Ӌ/eO>} /Anޑ`f͚{>F[J6lܸ0ק$Ӹ*}=gx`P)xҗig5<aÆJ1˘1%}zذa7D=ĉ^ƍgq4:PlԨQI&o+Ht]?^3mooWZUMݻW}-mck<ϱcr[DE/B{xyςHa۵kW: Sy9ju90۱c:qV*TNfF+WVׯO2ڵk'TsQsOn׮v[/s駟Rgh̘1]"f%KtOoڴI64)Zz?ڊx IG/x|Ν; ^*H֢pGpH…Q8lOIP8 EP8 E/8(zwQnKuQuAM0!I{ƍS:uR7nTŋS` P"Ĭ+Ǐn/]k+V."}*[la-1+ zI?SeVꞖ}$mݺUeɒE~#5E/γ2k׮uOKlٲI۲eK 8 E/xȢpGpΧQZ[Xބ :=78 EP8 EP8 EP8 EP8JX^\\\b8s8<FaOzHMpƎ|.ӵkWչsPDo8 EP8 EP8 E~pkYY7dʔbtpOri\F?ukn;R(z &tr ٳ^ elge˖j֬Ov۟mIz6yM)8JkXqK.eԧ?~>YtRo߾\z/ElZ/xƏzȘ\^}>p}ަRͶ/5Pbp ٳX8ډ$g/%4}K9(zQ(zQWƍ:udu7b8q"{Μ9X' 1[o߮J* *J(͛M/^X.\X>p>-ݻww2_O&|%?~>I;Ne,pfr￷@`bI!VOT||ڹs6_/li)\sϓOߟ}}VĂ&MhOo7ߜ׋\Hz1YEiɒ%@tb7hk׮U=zPӧO^͚5 jӦMI򴂖eL⋝d;ݻرccBĂ,zFzqqqITܹ<=">m,xoLKj EHC(=XDEpڵh:9‹Orlݺ@dQb{y쁢Cn˴p:u^<7n|$9o PXEŋ>խު矠ڤ!PΘf͚垟)S&pjݺuJ*I6mZ#G#G-ɓYf (=3&/H*g}Ve̘1VZeچi)- _ǁQSӦMV=B(=B(]E/q^7`du?=… w)lu?aС\Zݏh@L'˰age=ϴ|wrLo~K{'(-QNk̙O̜E0a,"=qDNYܹsY-<<[yf)tr-N ~Z||g?6ηoP/ϴ,14om-B): jժE/J &tG;N.@V?(zv}>K Wѻ~zpDыX3RpGpGpGpGpGpGzL` Pk^^&ϔ,(x7sp7 9m@ds0Q(zQ(zQ(zQ(zQ(zQ(zQ(zQ(zQ&&&_ 1O6F1l`c=#a{6F1l`c=ڰhu*qNZnaOWtiUdIcm6֟&M.i}D}ثUܹ;vaڵkr=k֬O=ԗ|W_y啷,H]7%z^}7oߟ~V @h{S+VYfEzW2>|@l"ҿεBG؋?Zd6]P!u{N<֯_y䑐[NGlhݺO1&^_BZR%amڵkQFjܹm9sݻ}jذa[nI28}hѢj޽ޮ˔)~z衞zꩀo3$zFK(v~e<={V >\1"mՆs拯^{M֬YcuE]ZX!E;3޿?>>^޽l٢xO?k=tOCJ~Hr;m|rf51X%8y _mʕK ]aܖׯ[jӑx/~M֒.^Ѓ!{c'j}?zOvi}vfy5>ƼmYak.YZ}?j׋/oӧOƌM%$$h/jgiH {H9l`c=#a{6F1Em؋K`~2@DmuUun@č;V%$$X 6a#a{6F1^&N ,~an:տl2)T:p@*6mRsVyM2'T[l yK\vM͚5K5m4ٶGS~zoXޯR:x}ÇW} `BD:uŋ}>lq^bbT̓j×&Mx]g^]lYuV|5k>}˫͛7ݞ@Ŋ*}:5n8թSe|ݫnfuy.]:m:M4Pk:W\>@j"셉񟇌;v}_|||e̖1 xmì=zߺ=vpiG/1kٲe@˗Oΐ!=[hQc韾`p†%5/ާ`c=#aƢ>iJEmKLL.jR`c=#a{խ[wK] p=@/^\>a%~a }=@@{Yd9>/j_ph9߅ 2.\xKa {]Ν;[ cǪ8P(\_IzjZ} |+D}D ,F٫WobRdv&N8,_{o._߲fLJ6=}=} =zx{ҤIM<CMbaQML;wL2GjΜ9ڴdYQtim6U^=F}XxeMZJ͛Wծ];I;UTQWyϯVXM_xQ*UJm=gU}uϻթSD9݊\Aw@{9_jٲz4o˵=}ѣ]||J&^?ׯj۶{{W1c{#Ν֯_*W޴iih'OtY!!ț:;|ʗ/z'ܡ*]t~ILL}wKr[F^|Ewi;u]407r Aɓ*G[n:Ĕcǎo}|ժӃRݻz3ΫVZWzӓ[! on2 DGhB؃fu`c=#aƢ>%$$h/^׮]UΝD-BG1lnNbFBLV:tHլYS~衇Ԅ ũDկ_?駟+V$[UUѢEm\ݻ'pں޽{N6MlRO^ڵ+'` %ӫWV/_6 bmۺ7{lj*m,xedO繜}ݧM+W.ĭ?lHFlHpl2'uA}˗WW^Uio^UPA]6[@p{FMvשWYfymø^R'6mdڇ-[xm ";vhQOl`c=] {qV Em@l`c=#a{6F1l,j^\\\}3}W d$pڰڵܹ݀;V%$$ޫB Y6a#a{6FbqGN:ݻՏӫ]v7NvݵkԬYTӦMS{@ Ø.=t.]T͚5?-ShQ5qDϪI&viVXfΜW=q… ׭[/^NwUӧOWoTҥ)RDM:U3FN[--AxegϞ}5o]רQ=Of&L,Y~amgСv-ț^xhѣGiӪʕ+kcg\wFg}J,d=2gg6muh۶vGiaOB)AرD+`ƀK/{/mݺ5Ɉ1dʔ)Ȟ%3CI2f|g?ϳ]}xmT8k2ڇSTV@{@ -nݺi#OȚ5}{fz뇷m'{6͗wm%y'Gmfu|ۤfΜ>=k֬V@h{5k\}?[!L%ӛ6m`e_H !al2u1A&rA|b ywqoV@xbv{'l`c=#aּy}^TV@x09r@˗/ ~z+g5q^ڴi^r%r= LnsVH)sW 2\tRF 4=Wzw rɗB5hР.l`c=ɒ%˼?ju?{¦aÆMf:t -󍡮y͘1cq.\pwL믿`g=a3wrSO_p=z/ϛ7˗/';_[̙/;wSz@/lNj'#o2} 2{6F1l`c= i{!0`7Vthu:\Tk#C2쾸@߲K.O޽{)Sʛ7r%WݰaCUO[7kpCD9]vM۞\p!sL.ڶmo;d> }.]h-ם:uҩt~2۹sg v^hQ݂ oKW^M.]+r;sΟ?lɓ<3+7y[[Yd9/'5j4[ ~48cƌ-Z.%T=@P֮][}3fjBq_Fzߟ:u3dy vsye102Ѳei*_ǓO>GݸqYlK9NJ24eʔVn@ɓ'sd˖K6vZ~<}-mlG_ŋmϟm}z86Jh4z?J,CHX̞Yf5h|}zkEDzmy9ζ}6OWH}/_H;ˮ[>OF $`x/Ipٓ owhNjo=rJ:}|5zݺuyk[mzmڴlפ=u(>Iz(_iuJFcazNrShxEJ I0Ο?@˚5\vK"`c=#a{6FK% RPT6 R[,Y:rĉWZUQFkS_`WTvM7xg'ȉ)U4iק +J?~\uQw}~qƩN:엷72wxIyŧEk_Ӄ_ҥ?_L.Ղ N2eʛ18a"< S,w^w(ˢET.]T:u'c˭XBFLaJnݪ4i,XaϫSN% ^ѣܷzjIw?xaÆ׭[Wr׮]:t@7Nc|V._F T֭@OիWWׯO2lٲjϞ=I˗O-\:n6{nm{kTR=͝;w O?uo ~^z4@T۷[~ 8p\%Z!2neɩS8]b]޼y>|8r= 3\oѻgϞYCz `3=#a{6F1B"{-ZH}V@0)]*Y@9m۶~G ~ 4h  x=#apB X @h:vɓ$WZy{n-2%JP;w M㼢E|NFy'aϗ h=VHo>ue˘:t:w*[{` f D=|tj|, z0Yf> {†ч`c=#a{@$$$h aH8DZܹY!z?СKC;{H'uB_|(Pмy͙3h®9{o?lȑ#y_w馛5>TӋ-+#*`lݺu2OI_u֟?̙3__.\x||5xMN&u͛7oڴ{-+WmV:Ox`A:P_}ֶޗׯiӦtVhѽl0ۏB3IH8pࠔi NrmQrc /نF~˭WAgʔ)>2}LdZz: zw}\vmذ\ %xyk[_}%ɵL}>A/61< Wۏ=7sm(%Jعcǎ2oa\1$6;h\ܹq2-ƑI1={xѿ!ƶeD;vlgYʕ+~D/ Lt$FwǼg>l/0[>6L0CzxshyYRҎi^D7```hlԩXSx;w.@4(T]`3=#a{6F1l`c=#a{6mT> a|ܹsuX!L%o'&&Y]ҦM{ʕ+ڰ'HH}^~6 {6F1l`c=#a{6F1l`c=#a&&&ZD#{6F1l`c=#a{6F1l`c=#a{6F1l`c=#a{6F1l`c=ڰhu q] 4pٍ9~aoXEj`+ݨQrmEQ~ib̙;x;cƌ\2ZaoذaVwRaUVSBs rزN{#_/u.{$_LmV @{$gC=ztw{d{e 4=@2=92}E*S/.e!Lmݺ,f>/9ߞw~aב#GZ!YPB 7ܠV^dbq!Á,{@,#E$ Q~$aBe?w0RR%< m 6Tsn@˔)lxw>#siٓ-IH6^|E-@$ |ZQ/c ۻwڹs*QDP}ֆLרQC:u}_.FDm%رtk{c K28:s뭷!!Q.<{t*r̙lYHv4˸?~fֆ<)R$vku7m۶V@{QBI*Wڵk{|d5KQ _*/Te˖Us{nu]w\nfս{wխ[7ի}̖-[|(_3FkgѢEjܸq~xg3F_ jժMڰdɒWŋOaիW/x}:tP˗/׾[fs9sfmײ'OTO?tޞ'msPeWPA+VL͘1#|н* x=k:??qZjXxqP03 }fכ w͛'w믿,==Fg{-Dnڴ)|_.Rv=zh\ru\\:sL@m 62-c 7_P^ǔAKJwѦ\:O \ml߾=>x6#_G_ˤ_ymF{>U R&ZF]4=#a{6F1l`c=׷o_>͜9.Ĭ {  {"111>ĺ {H9l`c=#a{6F1>UVmK5 4=O/= v>=_[#vL.T҆۷Glǎ%~oK? x=_C 鯇'?V @`{|y衇V"5eeӦMDeػzjT RC4@uG:\zM6|̙OMAfϞHa]rP})j VwREBB@{%LWZuKՔYX=wK"4hn_ìp6Sԇ=@t9|p>=[L?+WXQ=2/cH df!Wz%mϬy2d|ҥ]vMxw_)=@<ʕ+<} .CioϞ=}ڵ *l2+h K}d]4Omٲc~$$$t #{a9rjf_?~fk?+7<Ce -[6ugR?=@P~2NQZ#UiӦ>9bNC, Rl٭t[om9|r׺Zn,Wzۿ#G│'15ڐ}2g|̙3۴i3yڴi-seu9Ȳ@8Yt8|+u5نq@ӗ7=f͚͖<K.c"Ơg[ "kXo2fxٳŋ-{|5hSȵku_hBVZˬ4iN:;qƩN:Y r ̻{Տ?hqGЃl۶>(UхձcG5k,mcCgU}զΝu{17h!q}hѢ2Z6侉'&Oo{ŪptϞ=ٳMy>{I_=Ԝ9s,W_ %J$k&eʔ> aQkɒ%ZXdi=ũ3gӧOƍP'TԄ [xk[gČaS/'_I3kN:_~n*0  =ĴwyG_^U\Y/_^ʕK]xQIFN\-i&:gΜjҥ~,Ril~*UgΜ=?VǏ߽_dh…SO=%'i%!ޯj֬6oެ8oO-dtO~{,YOA%½j߾}Zx37ԙ~mK|_W!_~>={ҥKrVmZF$@>/6… Q>QElKUt|.DbaQMBwwГyCYjժFv~}ԢE)Q >\M:UK5$iFmذ!iam=G5k&? X(!!}:پn.FӮ3dȐlk&-(1BAbɓ'ܮ=In^ X>ocui`k{6F1l`c=#aƢ>gʰf͚X 2k,Y+H #*Þс v[ Q:l`c=#a{6F1F  ϟΜ97onuW4BAm{iMZlҧOvڕ>d޾}T"E=~ŋ… !Cɓ''iضp*]6w^oDa1EtA+t 3__WZ$L}0k߳Bg {2f5lٲΨQܖպuk駟#GDfUV"ӡC~O{~nsQ۶mSŋWM4Q ȑ#_lʕ+U޽kꮻRW\k׮Z|v@ ԯ__K !&4h@x2-[$OFGz.oԬY3/$_zu~do6{nI&y |$OF^|E%K@xFqa{6F1l`c=#Xԇ=wF { СC  hu?GT=ѧOV Em@l`c=#a{6F1l`c=#a{6F1l`c=ʰwرy9ju?,G͓;wc<*"!C /]~ {~V# 1ʰgt+TPXὊ {> t=#a{6F1l`c=#1h_vO]vM͚5K5mƟڳgJ6uʊs5p@ժU+mꐣ;5AcULm6!Cm5nX͝;W}gϞu֩u^ IF飾;m[o~UtiaKpB5sLm/vuVUl$!7TgϞUCM2ʕV=ʜ9{0Ay6[f$ajժU@ ĨAia6w$˗/],YR5lP5kLK[oU7o֖ $˛};`w[W3fP͛7wWLb$m43#Gng_$艎;K.3Zj1z{@ҿ =YlW^P4cÞO>D{g}.]곭ڵk1cƸÞ۷*mի_G3Wxcu8㈖!IB׺Օ+WLM۶m~1B9R ƃH<=2MLR?mE4/֭[ju؈=["EԢE./޽;}xթY8qҥ6b_<71j(ZW^}qYXkN-_ aٳgY!L5l}Κ4i\/BG7oޣV@>ϩ/_V@^ժUk}2=@QK>de_a}Z 2etQꫯ/ƒ̙3*\~'xGSRdu?`/VDԩS1NLLK> BSkV^>=z#a{6F1l`c=#a{6FhLȑ#y_{V@K8+믿9hР}.K(sǎ% !apȑfV7oϬnΝ%ÌU@x0Y ^6XӧoȞ=: d=@22ޔ3grn8{S7tӿQ>ba3<3 4=OrJ=OduآEB1l`c=Ȓ%˼?ju?{6FV~GydÄ y[n|{ƌ#ի˖-{H"GR=acjuuL7iҤSy.߫W| Czeȑ#?Y^L~OjԨ1J}ǍW^io6&M[p{H˚5s5wi3g2_z5>mڴ׬#8 a@Ⱦw> |o/eZBk6ӪWYfd68ѣOGoT=#a{6F1l`c=#a{@dɒe} aH87=@.\XoﵺCu̙.gdjժk]Z'!ʖ-Y}zݺuUիPҵOKBY>T҆ 2\^f)m+µM瞟.\y6? ^~L_~=>/%0/;f̘.v 1\E*hyzjɓ'i׮$"~vr-ܒv`14i\?%/Ko˺]rؾއnݺ3z7x)\ֳoB>Ν%/K (pu@Cocƍ+TPB\ /Yf=w,ۓ/_G9_ӳgQ.=-ϭy۷k׮wu+VTQrvo]n믟hԨl/믿ovy#mԃGyf߮XFy]z/&/A2exҥ6mL'ng/aO^\qe_`-DQ={ $y!&#P<ۖE-Uv_`.nj9\}`'~x{هut2 ȃ hɕZ^]rS:xlŋ3ա0T.vʖ-Uڑ#lSGa۱cGI'Mۍ7__zm*U5;@#Iϗ{%<=Oy[2.}pʕt.Wyfc(d=_ӽ> /|`eГ0!oy#}-m%ϟ?uJpk78ҤIb'_ WGk9Qsys?~fo}1ʟ?C /_oQ4 z}Y3ٗt'gΜ'>sz o#VvC) 1= 'ްad]v]kȴH[?f\\\b쏰gnݺ14UPA>qС0a̖g`f6syomW^ykVA$}*Al̟2e7n,ZbŽe ^*M{Wk5KLL:i&:gΜjҥ~ۮR\̙eͨQܖnZ}ȑ#a_bK֬Y/J@[hQx`… e˖e.~ǗUVK \^ϧv){s[nѾ25ծG믿O}Xfvu!_M7Я;v]n_re6mm9s&Kjg=z$t̙駟֦W͗p?Ȩ`\\ /w˗WW^!'`_=h-[VVZoe׿͚5H[.]J^s,`oN.u֕rjذaIZvFڵׯҥK$d >\M:UK5ڵk/_M{~}zs&Lꫯ~jժڐ,Xp98=g!eh"Ǭ]6<9zS߾}ѤI|/moYf_~8CӦMWso1I.y-[\fzЧiku? aH!F=`UV]`[hu_ayᇗXG1l`c=#a{6F1l`c= e 40[ocǎN>?`u`?"_ܹs;zh+ e{@OF$Y7#a2l0gu˔)ݻ6@ba`J]Bw wi`u Է?G$#|u. `K.Y#du`c=#a{*THΣfu7D1p2'[Uh$q8_l7pɖ;vJHHЦsȡ6nܨMZJ͛Wծ];ǝ={V}꥗^8ap8=8!RJy׮]S 6TsM_UNTmRlY0(!{ ʕs p2$_t||ڱce|M5a„_AlrYf%-K.j̘1ɖ%@r=>IN:jÆ h`hѢ.Ν۰RJBiwƌͷlRoo߾w…ĉ7msfʔرc;knц./c ~H"? <د_>/_~s5.+C 'momE-^oPGt!VNd!رc瞟d:Q͛0 Q+"̙3e7|U7>Y~/^i5_cǎ?`H6oF^{mL?snֿ}yfzN?>y>-6HGȑd (mڴ6%PHSH_f>&=h"251hРr]Fr-#w=?~Vۆ/iӦh NN>}ٲr=gΜǃ,pO>C2dHpGLϵkµOɓ'sȵ {[n?-zn/zҤIs]F97{g/_+3w}?|WO>_P.\x|,a鍊n7BOBxߪU)2[ HѻM*UօcdQF.@k+WdD7׫Wo6+W^Ճek̙/HO83m=@@֮][Um駭edI?dɒ;o^W[ .cǎP0?ck?̶SB׾y/_Π?մ~Dvߛm7@/Vĩ<,o6?6<ɑN] Ih|jt]/_-k6O΁hm[Ǐkn=_z`qOΝZCGN*;Q[8sQvһw7/<{ qƊr :D?Y'`c=8ށ)T(c `=#a{6F1l`c=#a{6F1l`c=#a{6F1lXwY!|:w\V}ʾaSlZ#Kԧ9ʾ a̮](Qb~;gΜ'n?D}۷]GxeРA=ΝѣGX')ao̙Vw̙3gmQ 0xXAj}zjkV@EeqV+jR`c=#a{6F1l`c=#a{6F1l`c=#a{6F1l`c=#a{6F1l`c=Khuoqb{@ b`A 1= 1 b{@ b`A 1= 1 b{@ b`A 1= 1 b{@ b`A `}:T~ (pO>íGjYzG~zf4i[{A2eJ֭[ju?z;O?mݪU)V%Җ-[VkРA[h1]-[gΜne9pV9ٳe:111ڵܹ@;V%$$X K?{)v]w_jժlҥY?{(V;O˞=}r c`)9 tM{|ojР 7gpk17xᇗx_=,Ya9<ܹsY6l8ץU0A K8o޼yr.>1 ([n-8/gΜ'8$)S,Ywb?ug\Z/=ACrӤIs8UVS 0sIUbE{GyĒ>z߯gnʕgU+%<{ӧ%q?pV 'a`WP!=sO>8Yՙ3gl9Hp5UhQ5|pմiSK7cR>zwRӤIcuTaÆJ{ᇗxj. ;S-Y$ɼ;>Ho^|I۶m6w^UdI5rHF9sk{z?_pAnZܹSskԨQ)8GV_}>+Zhd7oCov }mvکݻҥKӵ>Ȁ |I?nVh]֬Y'|ǫL2w}W~^۔פjr{Vjُ` 0 A@_G#نΝ;GU\Y%$$hۮxٳZz馛T^T_i~7M6%\dƍoUfMV DK?|̈́~iذ4h*RHL "y=9 p#Fh}rKA=@裏 e˖WPA:uJ=cj*նm[um'|RE.bA39 3.~PgQ֭ӦS111Q.\XJh[lIl0({<7m1cv_n/jA-+dٳkoFk#ɞ?A^xA*Uʜ9N `.?6˪Dϟ'Of͚$202#G:ꌃ22,7)eyŊ/_=W^Q:tpѣ6mZ ,+W.s Dt6h( \~=N퇼.!Co*y srHnٲeAN:Y չGuccW)-ê> &ñ #bӦM'IFnSN xO@_f}+W\)F3W(=ܓlNG~XO2d"{$YFjٲi\X2eʸkPܹsڵ 1YA=QF m@J2'ϓ' yJ4iC4'yݥ/2ICdo9Oe~I/u o&%'$O>cHÆ 9 :g}̪> d),0,{qcjժyĉڵ (ʡŋO`7̖-[yW}C{d7|SIK u?|p U|Lr Fxa+z㡇Ү=q=Of:ov׬Y<f͚՘="=; I RO?dϘ/R>1cFƎ XhyѨQ#m2yḾaBe/G<ӟ#90u} 'b Q(YrC?迨7)S~$D?=ޫ~۶g<>g 믦9Ռ{q :T`wVҥ azIoܞ+ˇx4~Ѝ6C$93Wsd94QPGtg fઑIҥ!Ǟ={6'+%B-fy!OrCodyAGQ9e@Kun69tYK釉a^_co( T `a6`$\>A~9 =L@^G6lZ4@ja`h9}|xE_9Ng}0b`/W^p r Fڵܹ@;V%$$X b{@ b`A 1= 1 b{NBOkڵkZo m:tHլY3zHM0!۫>|k ,P]tI2Dj޼y{EUTd?Uvd{fϞd^Պ+-Uj~/mPOhi13o>辯_~m۶IUPA:u}|x+\rٳ^ץ|wIWRE{^/^ .lNr}.:2Wu T4l>|P-RAJy."=E| 1"Em;= e۟{9U~}Kfd@88*z>۷~w}cǎi{#X2P,_xq}Cg`k]:uԦMܷK /he˺2,l٢/v>mԓc.N޼yժU3[_.н}{--+N!/=zZnzg cEɠ8-_ ;E\{'d$O60ׯgPAh=֛zjl<32$ƌsW_}=ͤѤ_@aeɒE}GU͚5S{i2xTzuCު ʇ̙3uʕ}.w^DR(U#c4eAm[O(Yv}95kVm:VK{ē!d2mOX6`I&i׭[vٵkz嗓,/]z5uɠ[ݺu2C_ [nEIp5x#78qބr<-經,PtM}.'kfᘾ"A( 7y ձcGs.˞ٲeK z,jI6ʞ{f_rDjNx&,!{\tIM6~Ү\.ɞ.7oҦM>|vK+%z-9o:~6e$curȡ bz&rѣvh,'(P Irާpvh<3gN퐭'Y[h^A9 $us^Godm"r=9X=ϻ'c7n:"3@f{5D6ءCtRm4OC?=c ߮|iO {HQtgr;v.'RGqm EΟk}J>a͛7\@=QF mZ'D2&hyPڞ3gvm6ոqc䗃~-!=qP_y 6ɯEiXWumirk6m͟9sF *y<9֮]+{A!i[^g+#t;}T32,ρ`\C~@eIC.:ޞg0]9ߞ+&*~>'")*m*)Y94X. ǑQ.vשqSo}HPDlq]gϞlC{_c*fHksO;>yc$`"2eeH_ ÇP^Rk|N? 0`Ab{@ b`A 1= 1 ^ %$$h 51 H}@ BVe}:T (pO>íB@b{@ b`A 1= 1 b{R$s\2`w HիW?8K.c rbiҤ>}\/X1 ,N>}CL.5h`~bbbU}3ftrq~ݺuԵ_{f/^\g˖-\X-l=auܹ.ʗ/իi=` ;v,wٲe;ۯ_'Nlo3Nzn?P}fp1 b*UAW<472`;Qb޽.F=zt^z<{ƍ۷o?Q@s~駭݁K7mڴwqo2phܳQQXNś7o.߸qYSLi6%{Zqtɓ 6y[ g|}eO۷zgrD0 `رc;weq zE^Vי ͙3q9϶qPO)o޽E!2PynE}GoӦdCN.rk=e˖ՒP+W>(M6<ߡ3gpL+"}-m| m6l$ŋ%Ϗ1cK&V H;w۷oagϞk9=p7SNh/zrm2(6խ[wv`WFrݣG7ҥ8tPŔR"qٳg?cO~XEwo6wȑۻv*.ˎ9}hk֬g3g|>ٻM^^ Ew&@ʔ)EJiu?L h"^0[]4Xb{{c7,I?[۽7ЬJ-/ԛ>:f̘ծ|&/nd'0@,2ɘ1c&kuYgy Mz 0 9] %Ċm,!,1(=ztiW\o+D(n۷5ը@" /b?cQF%s9grmrfm|߿֭[OZk?Oo?sk­/8&"O:uꔬ*E]k'Kz'HcOL;3L6hg{N-N뗼[oL0!y7]{eJʴC=Kk({oSO=5y&;;6Prޚqɂ .>g^vک@j[eqW_[5j@>4h:C==B"{sΝ;S.]{7Yq? "<ӧO2qĤcǎG:?ji AUlSdWxRK-;r-wy>jf%ӫWd-pyM.>OqWkN>4 eLِ!CmwVh/{-GM+Xy?mzeݻ馛.Ǽ~1 8zkPVK~紷[6,T*\k& Ə_^ʕk]v:;tTP9q[Th7{{-zMi8n߾}C&~xr]wչ#LAS?t+Ct+"p+g8j7tSzƵ- Yf[Z= DO\rɒ=ڊW}tK[o5=zLvg=yꩧ*>o9W_EY$/PϾ!᧟~JR_:^ _R瘳:+2aK_k&w%[ܶ;__Z=4k֨"xklϷb^j;v5lt-1$bٗqe5k V3s_ֿsQ '=IxŋVZiVhO{9~`rH9$@ {C=!`rHW1c(n1bD0*@j@ УG1mrHrAUZy7وR6`rH9$@ {C=!`rHЌ瞤 3zVmٳg/c p-U@n Z'N.#k =*zQF%K,Dҷo_Lb3ϬsG}:ϧrJ'yr';wNFY[nKneY&c=ӧdϸ?0Ywu!Cu/893N:%JrE%j.qp^uUT||걗{i%mYr''tۘk ZqnqPrK"$wqGiĉiA%\R>|%W\qE\#Fy9Ho䬳Jkdi/k EZŖ=_uhPCBAelq_^~w@:{?PK{wj+Onƴ[|Ex≒Bj:Ե@Nl&1wi iOe]6=]yc+CtK &4WL3͔|7gϱ7sE=PPД!+B!뭷dsUr8u]\ve ۣG}ٓz_5\97bW(V|s=lV%Zk\~ w=fbkB^z,k1LƊTϔbv[Ze=zk;S=+@u {C=!`rH9$@ GM>j@3y׫]@W_M7hM2 u?3owh:v8qܸqc_Z 0lذA|S;7L3}3hРa-\y0qĎ:u0iҤծ` Ǐwv P9_CI:jP;/1bD˨*ٳ%L`rH9$@ {C=!`rHЊz 0 9] 9'Vr=$O 70aBO&{Gz}mUԻ袋&Æ K~f9g\a믿N=>5ikjk GUҩSd5L暒^>}?N[_ /p" ڵk믿movҫWc;,;1S- 2 JC=41{ori[|œ{WhgN.^K6|dvJnᆊ5u-z뭓k=}7sOK/L4-ޣ7|38p`riwy|cO}=*֍}wM?77BZkw!dq @uʞxt?L3i?裓n)P+s=wc3oF`eK^gu{v(f+Ӟ^dqŠ=x:vI*_zM:wumK.dzl9_}?9Yqko5~~W۾j%_|E㊃RǕsϟŎ8+y_q".>C[ioBM6٤WU쳟?㲞>Jv[[&;N=駟NV]uղ<ڟ`;.9<8+ls0 unGJ+%]tQmP/Do8:^7ѫ-c9}' ࢇ`ki8|ϟ$mݒ:m+i&L0Y:L*u,м{ʲ92|A4UW]Uݻ'J>3Θ?d喫s_7{gONY+]qoJ]>KCnk1BeY6tkI->fb^_xdOg7zB[oݺm- @#*[p@hmI뗌92my?&믿~:Z_~%[ .(bHnybl~Lܹsһw3c5Ű矿FJva)]PJsM8qb馛?};3ӯprXa},rRhwNT=},R֕ǰm6ݥK6 qqǝXvGVZ@="ܢWT 1"Ԋb~89 70RYXvI'+#Hn8oZYg= cP[ox4|+Yj>WqWchlzq_dWNfy9^~d7N瞋P++\$si?!QGPX_:mq c+|^uc9s@D;Shq*L5jF$4V-Q(šSN9%S<<8ة/UPc+G,F۔l֐P rJbm;%3Ou\)式~;co!L[V7kB5jHdgtMɍ7Xrh_~n-Q G=`ԜQ#׏/g-ntMF- -viGVh{@YY>ۏ8ӫP[=,:mڒը3h{~mE}g%x@{&;ZUzSt ' >묳O4C5{>c {@:t0TPO@2bĈtvرc8~73U&.U |X?:t/fmۚ5P5}&aÆ 裏v=bjݻծ>9cϫ]P`4hаj@L;,r/~ラTa= uQG[G9$@ {C=!`rH9$@ h曙]@.]޽ծ_cСCۏ>S 65Pu:tT&NرurѱewKFٯ5@{&7v =z5iҤծ[oG~?[w%\olw}f֫uw{le]w.no~]k %SN)O??T`Ȅ :=#Nog<䓏VmM??⅐b\,dBnF.ۻeժ v={>䓹Q =!`rH9$ڕݻqjM!mΤI:R}Kaۦnzc=l73>nܸ:zW]h>lhU=͙s9PSNnYx;.R @^6?n:/L34k+?#Gz{[dm뮻wu> 3߽@+m{キ[q[};v?lVؾjYnw>:ꫯviǶt{@|Yguk_S /cnj3sqsoӟtlݥ^w'0mNqEY>`RBxw֘sg>*A9$@ {C=!`rH9$@ h3w~wk U˿PcjPoݫ5"]M33~[O?uB)n&/߿9mvڑzQժ v=:ww<\sIkSx 'xq{I>.-ѳ><蠃k >we뮿ۇ 2U(E?~{饗{_֘:_~WiJfm~嗳`hVn%uwnة v_;vx1ǜ|@W[W:ksZk_򗵫UWcZΊ+\,_9sJ3qĎ,N0`Yvؙ?Z[uU馛֭OlW~Co%\\[v;>,{爿 =܊RKk-7|?W<=cڀ]ëzq?[oušo9ӿ׿563uAQC=m~/z 6.=E/]t4?0}!14nGW߱_ {)~GGwqǝx ' ڿ曙bhg;vgup风:6#G3TIW _@+~zk]졇Z 듉9x8;7zfɆNB@GqH{ޓO>z>}moS_|Y[xSO=ZJ?OJy[n٦xύEXg$>_W_}uw4Cvuh{@Xr%_^% 3s=x-j駟[Ss]wmMU|vmwS=wF돞@%>z\і}Pi{ƱςjLR˱[Zܖ+l;S9rd9Y?pRHn喷o߲0D V㳡#p^[}ΦXeU)轚vm[> |cv)lߵԄ :e~~ѓ/ 38MA4_dK`Z;LWe93{/5SbdD/ɝw޹yF؊`1C=w%bHh ,lPTE{1cqϹJ,r/X.zZ}w}ŏ)3ƾ:s^ 9ᘅ1/Wk9S@O믒\}y\c?#y oGC>m?@s6I^A%{Wj:h{ nmc%J믿>}J&뮻n2dȐo߾u_zgoOg>c;㏛{7{XdE>;>}ݫ>~;=깋./"lh/?_羶4Զr|1Ny͗״"'xY)´/xgq䨣uq]_x+O?u 7SO.>OJСäw k h{Lٚkg}}!у>8߿CLc+Ct334S7Lv̄ />}1|5* Q_r Yve7<8qb .o--|m6wvGy¶[}^c|'Wz>hi=څ.t=dfFK):||_g}䩧=jdZk/{un1S[Cuc"lhrDvk']vYۘ1cc9n.ufmTqWϹJ_F6l? Z`JCbm1 Z6%v[YN a1]cC=\rd׮#(]9.|ۖ:795 VQ pJ*V'؃FU]c2%@ {@=gq ,aJvC45\ӷo߾Th/{@zzfq%\o5Dmر]vkM7d{Z&ho{@EW&MК@{&曷=Fc'vСä ?=ڌݻ]H_}'xb)6lsKkLN@EoofjPK.cwch b>XJvhfio]sW: {C=!`rH9$@ {C=!`6lؠ>hjPCN޽߬v-C |f`zuu knvuhȑ#~ҤI]Kk8s_~y^j`/ծ,2/g? -Th{)rHrAU ٳgK¶^xaUVYO"C?V*=0E/ ƍݞ~3fLc_b-@5ǾT EwxAW.'5V]0M>\:dȐmO>=h9=!`rH9$vgϞ~U`rHY^W椓NJ{رlzIǎK?'L%3 ۺu+tϞ=GW<#<^|.*y|giffu}tȑfy毛ʶ%^'xܱ{Rk>Ь"yG]o)l3fL,EI׿w߫SQt26BR[| 8+B{SUdѣG\hoےƆ544=E7T|ĉ;=܊+-[y睛~7BN8a!C:_` 77]ZW\y{?~N:M(?28wq'dm+<}{E8g^xa/nWo[^~yRK>S:O^:;\s}]֧O&yߥα˾/.W|м{@oA7|3l &tjV\qZ'6ls]wݵY"ڷvۛ?o_L7t?/Ӗ/ڗYf/°fDa_aRu4?ϕk#4ZtEisG`\P/ ?/첽 k-^M8_\G`׮]5n>_DPZ|\|k7.ƅL#F[q =Gu4hа칊s1'_pd߅eu uEWcb;n-[R6tӻzkU}ž>?4t6G;0Ѷj=WxlKQjW^kD>4@){v}b[ǟy晇]{wy:vر]K~ YAXJ=7=C믽i%6r.`CO{Ãf/?B^zݚ5k{'Xr6ѳJ‹ݻ8c.j\|%OZGkQ^Cz[K n<E'|rjEO;cr?[s&5iJ] EX؞-TȂ}S^ZA4W_}u^REoZ)ogVcc(:؇~vmwSֹsq^fv!V$1W_܎9cgoavf63X*>(W܊@-BЖ~jX8cW2;/¹.>c1L{8qyN9唣KCtzX-ΧS@;7sO4CktaRݻծĿs[oGXb׫] 0^ 2N\:h#G7x:ڵC9dD2,K/l"+@P/LV[jnv=@]B=^[o 83<ڵG4謳>ڵ#kvء%ЌFUf饗~ҥznͫ3]09^|.fԞCG/+/OO0Sǎ'~u@!P?B̥wmmU|ߌ3?C5PLz⋿o.l"VmPox,Qܾ62iҤը PO?5>-n?cOql5jz@s} O>czP=B=AO>ꥆy晇85@{'hwƎm.St 7Tꅗ^zi/U$hׄz@,oĊm뭷#_~ժ +(<*O?m_},ժ3(=zS}ȑjlz=6t!5tA u Fyǒ> mْw=_'{g;$[ner'<.zqqM6hd%˜tIwܑ;6` SO=5رc)G\_Qǖ>4hPw^/uY'|z^{O[n3Hki?sLoKfymrThu;{ImPZPJqKmAK/tAbԜJpb]&&{Gz{OzNHɆnؤT<tqȑxmvDsZlj{Asl`пOL➕Fq7\g.ĸv[o9_Nj^VʤI&./CuVq7} O82dHsϕ"-uL81YdEYf%yg#>ƍ_e*>c93|*yۓobB=ڣmPG]Z@{"ԃXLvevBZheI?|m=]wdJ{'Ǘ:ԶEok"Ћ)fĜ[a-H G}t˔7t4x_#-zDoJ]mՒ뮻N[+,zg&{ _5ڵkm{[$y';gKiɎW\^{+},r=Cj۞~ڟX_aUb,lntQBqsWrDBcbYim_ŖPNѓ1>q/d}Mۏ<4л{;{̩7ߙ~}۫A{ӫW۾٫Q 7B=H&u2s'_p!zm-uz-L3͔|7 >G)>_|t/zieq/!*5FbfulIz;⠮cÉ0 3\"*Cs;²ioPW-2 0 3O*aQc>[' 1;/. BԋqͲŁ^zE^{5>AZO?UBrXc5ҞvZ[1<ŵݻw"׫]Pgbh]1n&BPt@Jܕe55{lJ*y1oWn+sR ކ,xk"l Y),gZ -P9c5V5s1ݗfSoiZxdÔ[Z%uq=b|sj}lHߙ|\ڄzP z0^{-/nY[i/ :o,PhK~OeWTj(; ͚SCc(w)'mA=V\qŴ^ Ga"m(ZdmC4wަw!YNIKrdD,d]̇W_Oƨls. a^xa?Z2:{>+\ao~t9bɧz* c޹^KSD^mQ3/z<ͩ1;ӔU߃>uh N;m^]#BdEW<,|رROs.@lzћ.澋oRLK}ګI5yDjȢ.:&@;%ԃzO0l~ .`EtߥK+z[{1^TœҒY^uǏorx"p@o[m{ZjjL̝^e,式@1DϮR6TK EX@!ߞzgќVRG F'J͡ߙJ?Oy ł"k9ycnX7ާnYϝ){EP?[-KbՒlY>?ŗqծڿN'IdeMγ'T.›z)$oy_$Æ K&N9_c9bX#V+‘]t9aMk6DXf^x?va{+">^S式՗O8z_~io9cwY'؈M'Zw\4QO|lr,Bsy畜pgnJ?|pm!COyۛNEGhfe۾YK 4$'ď/_j#_.qfvu"0[Rq-~M2~/eCz2N;G]axe59ԋIkFr[8Ò3I^x-,}\s5}ḽc_Sz|aOd76V 1l95̔܄[}{>;)isNY]rCX`SYgu՞zzІ~>Vec߾YhObra=Z=>zXB{ֱcljm&L0$01n6K8∤GPX1+2uxgӹ#cbx.R2(sMb*zFtA*!l7~ pߩk6\N|&rHLPrF9#@ gz3B=PrF9#@ gz3B=PrF9#k={VPrFW]ZkĿ]@1^~n;w7qĎծױcljojPLWG݄ :U7PrF9#@ gz3B=PrF9#@ gz3B=PrF9#@ gz3B=PrF9#@ gz3B=PrF9#@ gz3B=PrFW'vܹWZ^ǎ'7s] @!^u@ſi] @!^~׮ծ߀@[$@h.fԳgj0EB=PrF9#@ gz3B=PrF9#@ gz3B=PrF9#ڔk&:th2zjҠ={r\aoͿby5Ҿߩ/۷s]ֳRK%{k 0* _|}i6$2pjFrWs=QG;dI矯v kh9a_qɥ^:UM7ݔ;yGu]̄zg=1[Ը袋;,СCK8u\w} /ӥKt~%n +2c=C=41bDm{}u>dV|A-C -6t%JұcJ_Jg}{lRä>裒_[odȑu|u|4*=%o:m;oOv?,䒓G8O^.SzY Cv xo9ITzsSO=.KoYgy&i|0vشWM i2dd/yG&{ޛ|u_|glS4O<1ꪫ~z\9rj 1~ߑ]&ko'zܭ\Zy>'!.Bɝwޙh;9^F.!~o5\3GP\09cUW]52)tm/ӟcfJìxEYY*,L2f̘4/2K=Lq6|}I'虱['7x# 2Qcl7BYg5AO?^BY0|tλSOMQ7}䜒I&%[ne/ S|L7yg}6}SεD7jԨd%H?/b>+U "+mI=ҡv08GE|V 5}l+}`fH.체fmNWҿmA9cIXW_MAk=zsC,M~WP6ϟ~/ww\P/;1 etiso4D?J\\zEpb+L1|%W\qE7ck{Fs.SOO&f"<>{gm\P[lqYܢ_= k.yk](,aq:*R{lD@=ƏL3oj_G3/׶E M8➨;jj"~VkJc9k,\"D0U<iK>6~?~9dvH{E箻R/+soP`"#bUS'zu* =w}c@&4fJ ?qãj-%*7F 38cςz +eJC˹օ@/ZvJDQG8 JˆIk 1$9BEͭj8Ri}r_6T9b,@lJ=hLfEGCbc|ыnK>:˗_~j^z}inSC쳟J<Ɨ9#.z:t=+$oio\ꫳW_}.=^{;7x%׺N񹍡х^ ;-SȂ;GƶBSzۚJ^ccc[gE\0ӦR g  z)1O?m9WJVk}Liv1\/v-y瓓N:)Baqѓ.i;ӹE< "R:DPZjQX< R_,C7kR^̏WR ),}Io=sUTۊr_jZ|L]zTŸq},z0P<T|YV8Yw¹c/S<>z4vk(14°J{[R1`waK7`:E/RA@2CoX|9նگ) /VGK^aȑU ԣEU2iV[o",lhxd=:wZib)/>3<1Өoya0кƼOAǍTJ?c06V,QFS]i*.R~VC%cc;4XS+}"Cjу-\vZv Q 5%sn]wF?mn@9s07|.Mh#ى) : 6e]wM7yUsq=\3SR *GPhOz3B=PrF9#@ gz3B=PrF9#@ gz3B&:c]PFUhgz3B=^|Bծv ńze:tva9ҤI]-CN޽߬v-ńze]|U/PrF9#@ gz3B=PrF9#@uqbqۤI:ThOz@źt2mر]Q 'B=b]vM-OTLO=PYgO?ըPX?p֯Pⶏ>hjPEq_|1{5jD9#@f/L[jS+$}G>mwufmMժ vB=I6|;C;c $[nGyim7|7pNժ vB=IYdwƏ M6 3|_c¶zhU& &ӟtᩧzTaO@MvqǝX=3T &֭Oծ,v_jL[vYg qVjPhsN~,l;#N@b}8ԛ0aBjS3Т>jSlN9唣kRvᇟ!%QGujqk|]`j$U=Qض˿.P`#+ĉ;vy\] -cǎǍ9ծ%O{[l >'MԡƤjS^u@ſi]KRK-ԫڒ]Lze_VZV /S ^%" KPhvEš^o~󝠴mu-N71OժجW_}5KvSN]tw8|=5?xy??ZZPF]hF={v Sw]w &t*l0`YqߜsYjkHok.^jm?Š+\|eO?Fmto>lθ/GgCq9/y}Ə?M,px饗{_VZZPh1_1䶸 O?ը0orm{zժd^\{?OwQGZPh1305v+u_[[G}tRvag^{pwI'ۘEϸ?|ZoqXe9?zƝ|w^eUik5j?#O+uzTS?S}^CßO9唣48欳0HVmPhQW]u-zV#mM^`=aó2W?Qoa[p74L3\xDNpֻkK.dgy毿曙Og+ |n6TZ"SLC ep 24svuyc8m\/W^o{ st}ز1z{)--Mǎ'7Z~S{\0'Xsu}4nKޟ 'p) }EX ';'v, ]bE^l 4ڲ>䓹b zR pP\ZhG];޻&lrivdބrF/ݻ˾`q  *ìRbjzŵ?} Gx +<_؃oEy7FsĐ.>z> B|בּy/ Yz_}a6x{cs {7fCnjӣehۄz@?gJTsEܬWX6zϫq=ܳI8ӋC\>S^y啥Euܩ%_%\K6<#@{$Zox5W5X#Gw޽{Y_ؿ M\sIq[Wɹڲ)؜[-n%x 4l-fM^ԋP/lRC?lνJ4tަc9>1ځ ,aP9Тv_JGgc9kGc_ܾ6Zm՞*<6{}>}QZ!ؠeN?#bPO=ԣR+6Շ~@s=-fȑ~׮6z~?>G/؟tIǞx{ 7]Zs-RX*抋9C= ˯շok"ԋeKz1`cǐX97z+% "cݖ8?@[!Z~wq/b֮b^zi-⎸]> 5wyϮj-\p[nPֶ/PlmcZ yǾľ9F֒zw=ܳIu{ 7joglqJ oz@pnm֮%:Sc2˼\Zh;z@9Yu!1]\[#<-z|xe~4ЋsFMoi[}YddK;dK?:c#馛3Hc9>Qo|ɩb[h+2uYcuE(Aeʽ!QF%K,Dҷot;,93+9+nk.F-OXveu|cs";#/8o<}kv0ԛ8qbZS/p iO7Xז[np#FLVwl{kۆ $,cǎu{ ^p%OO?pm[p[eUjølTrCʋy[nI{qB=2ܢc4@t?36\2eblMg}vɠ>}dmY9䓓a_+0+ ꪫ&oСkmb|q&E0~di?E^,DX%M3<5JPf-wrk^}^Ct..ۿuӽ{t馛&;cˮ:Ykҡs1G>$o}6FculDHPeY&gpYz}3Cm czW`,-q&zhcchkn?y[hh ڦ|_U( @bT[0v>tY%bQGy--Ă"+rrOc̑GgV[%v[EuPnn*q(LzaJk ќ~wgNvitQjk K)dŽPB U4pȑ#XÔQlnS^;<̓s1:ps:K/Ts&P'v,nرjPXE2Q 'B=b~mB=hyB=d9hN׿+n@3fL⶙fjPدڵK.cQ 'B=bcǎR&'*6nܸm;wWZ=4iRjPrF9#@ gz3B=PrF9#@ gzMԳgj@;#@ gz3B&=ztKY PrF9#@ gz3B=PrF9#@ gz3B=PrF9#& ɷ~=ڥ9B=fԳg駓fi;&L,ɰaÒ߾P]t?|Khz3B=*[#8"y7>}e.d/}'y&k/O${GC%7|sr饗NvI^8Y}Փkfsp qu]K,nݺ%[our۫Wȃ JLVx3Y,/iw'K.dϳ;m!snKƎlfW\<5,Vεh˄z-3JGǏOF*$zjrQG%/re4Ћ`sϭm|ӭ>>~ޫ}&K簋g3ы/l&sĉɋ/0 ^,R6vxco3jԨt_HFMq^2khzUviչ6ۤA /P'h߿/ cJCK[otV#y~Ĉ%zO>90`@-reX?8xwޙ7.ܹs{:@ (g8RK-Uv }W;%k-y:vX[ϔ>ztպZkMv_E=W]u:5ZuB=*9Zo)sW&/x:OSgҥdmM7]o, 8cbY?0}-}ZPk׮>ӈPovKo}Ģ>t?PgyI17`[(/0;`j$ȉuY'yǒ?kkopZ6XYnHfqjڹ[cz3B=PrF9#@ gz3B=PrF9#@ gz3B&:c]PFUhgz3B=^|Bծv ńze:tva9ҤI]-CN޽߬v-ńze]|U/PrF9#@ gz3B=PrF9#@ gz3B=PrF9#@ gz3B=PrF9#@ gz3B=PrF9#@ gz3B=P 'Nعsqv--cǎǍ9|y0pgqծP ݺuO`jo/2mk<@ >|avsYk믿vv -5 W\AUP` )q9#kѣGW&ٳgKU;dĈ.AB=PrF9#@ gz3B=PrF9#@ gz3B=PrF9#h%+B&Gv)PZIϞ=Kw%yWiiYo@{I_8 0`@\}S%0@<Ʌ^>c=\~^{USNc>(q(cob{ł `C{+*"*{;+6Q]g!lfu5df2 g}[o4܌JÇOsZ*+v`Z'".=cSzw_r衇NmJ)Ev&GqDz{eMYd䮻Jooɐ!C*_[.)+wGi 6lUYZk|SlE]sP9W\{|,jQGySlׯ_2`ߒeYfCM6t);<ŶY}umO?VB>mݖ\}S?gN|#FHN:N^zC?-ВȾt=\sMnJN>}ɭޚ,BiPAKp*WOl}м;ɆnSz>;z:݀ */3fLۧmm۶Ɏݵk4-ɮE7&wqGkM+*8);/r-ӐJkn}#h@Oߗ^z)c=Zj2nms1h $B=(>8]&ݸ⋓>}{rEnMqp-$JsTo:ɱVp67p )*lIZ-V:43J_O,ogm/)n1e>hŶqYkmI0_I6mJҐ1pnݺ.sC;.]_tEm6ӠO?cbZYLN-B%2**l=z)UiafJYH83-˘Bo6b8_sEWݖŶbm5֨Oӭ*iҊ椺q]vzs`8B[2RK-N1-PXn뮻.-y۳.ն*1YCevT5ڝ/۷vej]Or&7qtm&LbomxgdD_6YIs5k3&ex`W_U鑯u6mU@K""1"̋^TEѦtD[^;Fb~{xwYpu)uade 2 #tK/$B&4"}11'"1Y|QgB*&8bqY\TgU&j~I&5} /pZ]34\sMrꩧN=o6J1qF~`3ĆE:1V9眓e_6v_}\}N;-TOl_OEMd;skL3\r%ɶnti$1m}D[La?^=*Bd[nac*s}%ݻwOCʇ~x":ꨣ%6mݖSR[c̷ѽ7 QQ/(,uwܸqi7 2blc/BCPs].b"^{vt9y@3LTzi{4*7ԋj_>4&ޘbyqQ5zwQ"3ƸrU6b-V6:w 83sO5k8'3Ɇzkm&*Un, 29}*=l(dgM7]v +jE@E5ZS|trc&?;qiԿ[et/= r-i?<*?ƔpOLy䑴;hTnuj瞛s1G>ǎFP7`tx|޽/fs5*ګWb . /f_W^91QcXWх9B~51vu]op (F[@iҋlҥ roGWʎU^"E1GM"+w|=zHlvRc@瞫վRKmwڞ뺼QWdD0 n)F[Pɓ'cE%#T[;[^jN018&{B3TVV֪mLLst~R/oҥKLم^xD?"4Z$Pgoo+hz@hӦ_nB=N.҃9-R :9#.6xh TB=N~g}.F[v7T6B=vmp1-Pgm~h dB=V32[֭'jժL-;18z@.#+ޫW[-P:m}^B=G}ymwoGTT_b?B=Js1m'7h9z@?!E1?B= @ 7zb7Yx7m:tM`JB=#G -UW]~wee?~n05w{^p?kvS@ :wm IN83{%W=z衇?5Ugso⋫Om+'pVzJMB@TkWq uyVe]n*KSZ"P+ -Ko6{0?bĈ< V^yW>hIz@<]ve|߷*_1-P:貟~iO<9!)F2`3njXK/`P>`VZog)l1-Phwr;n 3^zՊ& wZ@K dfmF%F%F%F%F%F%F%F%F%F%F7x38nԗPJPJPJPhxE;_.;wzn/Uq7߼fm}~Сnݺzy;ivzry%z@V[㏯3f;IIݷUb_X~Ǐp7>'}uu1bF?vbz@ނ .￿gw}f/:묓|ܭZ冃+>`}P z@v=mĈgo{un:uMU>餓nʽ}wRK]sw;?^V@%\;+9K.x`vۦ~瞓feСʶ?5@! fg5|7Ƽfk7;餓xV.^zis=W|o]wmy_Phfi?:蠻ce]N;תn2V[n9]w &lݴ%ae8BZ[ǔIP4B=Y8q3mvب<端zO?}??ԋ;~s\1~6tӳ⾡C^R&/g,*7fosE(n=Zv@1f}W6FU﮻X, =PJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPfUv)v;JP~믿D@Q=z_b2+ҫn@ezb-6&D%F%F4،3{s|;XmiPh#83nD)\V*`p5ZZ#ݟ9CucD\Zk toXӵfk G[~z<kGixg׉+{5ePB=Eމ 䯿jS~?Zݺu{4wvmw߾}[Y^zty}m7JèЊ*;V.M8K~)q;g>!CߎmλkcdbU=o5]wgl|7sW-һO>#s⊯+5uc]pGf]tnjXv;^w#꒻->Wǒݎ%\}jsĵD|d5,RUI_(~j7~,hj/U#E@㎷=u}fe k#Gqa|}n:,ԋ@!#~Bk.Ĥ& ,m</02^-RdI}]=ÛFez/Fxb,G}[e&u BtueդDi<&8wJ]uxqNlg5&'ޏ"dM9ϋ!M%A|F}swmr@/&Yy_=KC{M7}811g}`uzK,~3ףF_~m{]k7ߗq.q׷-4/B=.]_ko3PvFr^}nHI$!xyuYgg稊s뢪Y=*Z**#ˎwE3{l۪^Gm4Bد \T;~~ٲm\rɡQ}Cƨdϱ#{oLEu\>qEEs=v9ʯr"0*˶V f"B95um̪B]uĆ7=bĈs?^8BvP/DWz8 #g=7˂I&2~43QQWdžxV{z=Ť)!~,ΎQۉ2zW[ޗBiP(.]|4vEXh{:~ ꧺ/ܱ{1,BO>yPE^rigme򃑨?nVYq.첃5]uUfP]]_Ycml]sÏ~]~źJl[eDXUkȭΗE&LQ3egameqA]֐nYnQAtu1|C5.Xmz@ADM}ZnmNabUcj>!Yxz7? fmQѓ?!Y7**kb}Xϧzj, Q+waÆQm_!х2ls}}mك5>纮bLc927jkzemnR[|/?0r]Q 8<t MqPz@.n =KN9唁YpRLQR1Zhنs=~יRO>o]z…(WVcUuls2L3<1vX6vZ}BTjFզ-ؘ !\Eǎ??~B}|Tv=W5TrQKQqcn@])b{ctٍq3c29EJPhTT/ t ޔ*1Az1}ֽ7j#hVY̘7:vcưʨ1g pF0YuWR[['Am>jܡ>纶B/'>9_|1ܱ[1HtqͽVz=EYdu]ꫯbӶB^#>Ш^+nw7e{)&l>+x\1kfꪚ1̽"TʤSyƒMc-~)Ř!ϕ19sl}#G-qQ1q :וqc]z1IuUa3 8sm6\O1uz/jݟ] 1LU(5B&fl{tu㚕YeU^3fA=ܳUcb<13+wʺydHDZ1l 6pSb09v}mVd* -~wy7PGUV7&bmEoQ9۞~bUo=&&12d!5m.Y]sgX<5=.fm j+rg mlq㽊.Y^?SpqYwUW]յ-l|F(}B=QKdcAcOSIR'?Ќ.uSin@#5Xʎ]Y@-‡#~n Čdd\qb€8VecŅǮ{キG뭪\r:tP<2,v}Uu =Όk?w[t9ϟ1..rюXr﫮;7߼S$n٣G{CN:X*ۿ.bOB;~YQRKScb6rsT֭&L0Kt[=3OI,"Ĉ " %&2-&]r}Ǯhˎ;xw߽k߿EnU??ckm2&y9ήj2:<~]ݭO;b6Ԫ]s]'OncEecL>3{RUP:uI,[?v8ahvێ T'*<쵗\rɡo2q^znuUĵb 8>-zU~Ageb+o uPڄz@El\JhO]gd,uyfm`]*Z7XW6_TB~RDeTmz}|;}.rS{38c@,?1k;l]u]kcn{֨1N/*'kFdUɝ=6 >o#ԭsq+:[Դ_MHwpy7ƴG4XToJW\b+bLYgb9cɟdw>m۶)z9 ]=C݋Z\!&nGwbc+Tmp=Q z@E5K[bk׮{nEW_um뮻nϪ&($͙Pw饗KqǟKQbXz(1c,PvTfVz)p-EK.vꫯT6TF@Q-#H,jժlĉ[vS6TE%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%lWN~䣏>*vSvI&mUc@!f{aV}tPrG6q'Y|œ'{.vs19S}As=e]L0!oZ+ @fM7tz뭗vMҥK2|i.Z&-Zk|Sl{衇E]tm 1q۷o{YWsa3_~ʿN"~VX=z^^{u5\sAOwп#ٳsW^yE'@ E5YСO8tHe]6ҰoM6xlfk:2@^b#G +Ǎலp,L^hq+W}UDiw`.7|\gu?_1`9U|*LN<=bmիSTC9䮝ws_tMgVQyO6v 7t[ꩧ߿{c:w|Ygէ^lbhzLYKw>3Sl~:ǏaC;6ht}WNX k׮s9H9cM1l1iNGP +8B_~yAO5\?l>_߾}2d6]v6w-k54--mݖ Z/޽{:?<:uQ@{衇&\rIs̆;jԨ5zuwܸqLMXu|BC\Q뮻VOm];sꭽr loZ?3+}'M4 /u7~-w{OZjOb=hР]sC6P!&7袋cEw/b-6ն[.Xr=S܎^_~t}cGT6lemAY励pfޮk#/pfkc駟^}f6&2zUo6Gz 94-ƹ瞛s1Gwy.*N?cǎn&,"c`jB=` gqƀbPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPJPzw^|Ň*v[u.v;ZVZn=[ng{y]LI@ќr) trP0y?v衇^)vE$J^v%\rh,Q#lR@K&7sМ<ݢQznw= zٳW\W_}ubZJ>}=zt[oE5vE{%o^Æ ۣ}_{۷o?\&jhf8b7h C^X6|=c[)x 7V&Mj׶m?ʵmږ@%jIJbͿ?SNT@K#jm̘1vm;>ӎQ/F%u;Gu)Q}G}yB=(<Pg,_}r߷:<[n"4 Z P//vsϭ]6@K!mذa{zrÊ$hz@x≧;1w۩zP8B=AN;CO>S-PJPJPJPJPJPJPJPJPJPJPJPZÇ'L>b7'`/brq%{rA%[<85jT2t%zJ92s=+R??o 4(9䓓K/4|v}g=vd}Ml͙/]d%k}s4<׵k?-um-PZrC#8"@O>-W^If}qƥo.lZV>äUVt}M7vmW.?/(/o`/xSK<ԩS[,b-)8sv@/;ZPQ[eUz 0`P,Yfe*йsCɏ?XsKS]amMQr=ݺuKCa.)nO0!~]srQ-RK-UsT2B˧z*Y+W^he@ {׸OTj#{%\1unW~-Իk=zL}8W_#̗U {UiսƬCMx㍊Y9\s5Z;hYzB-"Sm&O\q>K׋.TfƏ_eWygm /p[oMzYmuםb]C~W\_u!\~8ݻwov0m@ 믿&:r3fV0,Ի 1 GUmW^9.8g}v3ϤQM7lBWxkvڅv-Lt{twUv0m@ O]Sj'x"Yq/&NXiӌ3ΘPA<m6?*VVP;o>cP/Ő=`"㏟"ԋB\0]_veɑG9cR0 3XbOڵkWOj# G7@mQC^{a۷oL;zBI޽$}݊oG1~;crm]HG_aaV{ bn!YaҰ+/’»&OYtME]ޟ;o+ ,XuUӊC&wuW^r% lAzSNtBiPZb܉bvVZMYgK!ܚkY}vKgun2-m{UVYe>b6f5+TO! 2b&g<El4B=h?tF%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F%F@ҥKb7@@ ~\B=m۶k[vԤ_~ zͯ:s6mWn;餓N@a{Vnb eB=Q+j{z街,o]0-⥗^ZUVeۗ[n7ǎ /q1b9￟U4B=ќqʝ}E{뭷qo+Fj/ ~zX6M "]m^zWcGgv9=t!2$m)@#,^zڥK>EѶDeTtM=z( :^7qg1c,ݹcgl۶qロ6ӷi2Ƴ,tI4>l[裏DVϞ=Gq}+ֹ]W\q{%+ 'Hc[n=!2d!|Z<۷o?j&㏶L4]e5g6^޽on,ۋ.Ͳ)/Z} ,U7lذ=_|1=wMk믿g 6x273Z7v:묳;STc/WtP/*_o߾~Mc3}Jov}\knRn裏va7n\N:}{YgGKT N?޷馛>#l~z'|r 7mUM~GKvwKWv?mqʽ{x,ԋv]۸Zk=]M' ߪYPPOGTTm^rk?_DfϪb|,̶GT[]Aɓ[3Q rUb‰hGCw}[駟^1W[q"Ћxsg}\s^TŤq;IΝ;=6ϱ1Kq|/2˄~mFB O;n~oowTIfϕ~[kzumw{|ɃxJ'09g|F+{lzX\ l* jW^ye }u"@- " a?<[!х17H oQ믯Pʞ+W[eJyۮ/>>W^_m o62hE`?瞭r 3ֹ^r-V~@/DYt-7˞{W~% b-6&B&lo}憪OCTz!0G΂?q Ͳ`.bB*Mo;",l-? ?;Cc}0UU5ċ?.;WMю;v",똇rȐXE%\w1nULjsRn<_tAP/Bݸc='.Gw,T{etA6;DUcuǎn ;7˺wц&B= *+&1Y UMTPHtr5\/1AVCY4dܘu7&X]Bn;r_Xfqc=bĈybvϯ@N\oۖXbcU} :wRK-N# Ǣܳ]cz{mPiw&& 1FTwl>m1&lH[ԆPht tcnOU573ܮ:t1օ B-&9ccM7ݴK~nG}D\Tfƌ8~81bP]ki>1^fFnf&8#fou6DVfzMt HoSSgiw6յMmƏD 1pl PhTQT]׳gQwqvM٦s]wݞbd])xc=E6e;c#֗_~|"±UVY,fm#|u6v_ 17cŤ+1p,;bL?BhHk+BW˼I2F_^1QUM-!{v&Xc5^hO̢5e;>X<6sm6w!/*Ŷ}JrXQW{U>O<E߰aۗ]vA 9P7B=dԩ'QEUU8C=#<晅;J_|q^vq֨x3~khztQܾKlB.v;cO3GHcŘ=BEYdlw) +¿`d1mtY[,&z.뭷ӹrkK/Zt&!9\ѵ5fS>ϋk*jC=qczmjẴ>.{u&uPh1=u笩EuFx=sk/ңsEPd~7h=Y0$[B."8.T4T/A梋.ᥗ^zp,5#Fs.1 SO=~~\s^1>^̈;m?蠃.º|1Cq6Kq0/*;gbfxs#}ݮK.{ iw]{ǔ;P3*Ubv9[9 lՉ1R Wת_vI'tZ#Pjgd,Uݟ_uDKMEլjubxOj׶a6{c9>ڭ.N4W_}5oeY-4m_n M/:*hiz@e1j4}41>_^nc,bZ"`on@F57n\b P;C!CԁP;Ó=z$Æ Y=zt[o,駟^f4 Q,; ZjUV@![noyG-Rzꫯ~WW@K!mڵmD˜s]e۟| ):{T1-P'eY&Tu?<[SZ*P+[m={owj@K&/ܳgQol޵kw] hnhnBѵjժ.++J=Ԅz 4rb7"¿}fbZPg|C 9J5ܹbn۷/v;'#/8쳏ϩxJv{fy뭷{v[njժRXꫯܹ?m4{y=dȐCƍyym0~nbɶu+rN:}R̶@)QGA\{/r?m:xs5׷l+zTX`>bfyGyǏ_hK/=MXL3ۨQz_mvqmj>GydLP+mڴ+ {%W[m&L0KM¾\1c,EMNhuK/ڻw[n^5/01vE:tcSz@r-u{UMO/B?Ewޯ}М(m}k׮﮻ԴI-?~fh4Ygu}7[a^c=g)MIPPnmك6nVY_M6h*;묳X/ >ANԩ'M.h*[~aSn袋 hv@SQ4?]~y'8㌿7E)(` ?c*TܾK7߼-jzÇOv%{n[ӎ>}$ҟd 6(nhRksKVn?s+b(k@)hV^y䧟~cfu裏Nve4 Ȇ -?o J?,ݻ-q4皚92vmz+k;/ٳg@:C/u]oioy'zLKZdEGydm &$cǎMxnH~ON`Ӛ믿P/ M.6Qzkk$?s(:mnMdw_~y;vrcmPHtM>J+. Hw|oW^y%4hPEl Lfa/{Oydy-rh ٳgQrKz 8)X:Y`**krI'%wuWRVVV~Q'|~d=' /pr9礅6='8c31!?ǵn:W^*RK/M6|ZP>OOZ߭[+v~cIJ~…^Lϲ.?I=ymtMqL~5i?<ׯ_zToO۔ywW_}59:}'ni/'GqD_TzVb*dv2[z}5}ZhnsSמg}ﻆ*1/w}g|>RK-~]tj+gNK V:*qκtRc<SNOػ؆:9묳*nuOOS^#y,3)5ӞY/Oɓc=Zvs=[)`ZXZ뒧zN;Tm^7rȊ1JF I??41^.Qwԫ/bEЊ+ꫯ1ioj{S=6@ޣG>ض:mYzB7x#-,?Gq^stz뭓'Vlڵkҷod5L{5~wkEAM >qA3&XbdgNZkP4?ԩS2[ne{wz h瞛gzvϯXQbFkqU. k/F(=W15xo>Ctx47tSW_U܎sGYv`-uU1Z`T'Vbb%[,-b;f:\QEv[:r`;"_cE1MV7'O ;~sE%ofl(&cDL9p*M3+:kQi,[Sc9&-QQEbQiC#;,4H}?QL6bl? -GIMc=b=T & tՉ?(.1\=CO?ӿ?$FyEOLkEnDQ&[ij|LLDsիW'H]vSܞqS {T͌3&)FQdҤIsF:1ja & 21bU&[}/W]Fӑ⵿E3Ojܦ{WVVVq!E@<֤}<csIU?gb3X򜔂<1gDp|Ŀ%ٿ'!F<~5gXOP6[?S@[k~or5͟b=j-FnyBcoSoƔrj[_VT7_rۜ_Ub?uYz.WWǍd`/sڊCvdjV7d*Fi+cz(,ѯb4q~Bx kPǚ/ "Fx+ӔRsRjq>(/F-JP/ )F١C"Kת;STMAuD1Jڣ>~bܑ\p)#OIZl SH;v9_SeƎ[fb-6xObD*b6۬vLsxW;Um֭[yUʿcb(ލ%s913{;~Uc]#RLS~?5sM/'%F{֨i5Xㅦhb=j-Eu]?bqWÆ Kf)#8|W:ոq?eSiӦыjzX/^\nz}`!F˝آ"|뭷?Zɞ{x 9#+B2|MzE ٸ뮻yUQ?䬳Jo5QL3?HveX6b(:T;pLe[nfSu_~i;C> y,3 z^\rIz;>:u{:YxeMu1>3q]eo|'~SsRۯbN|AQMq>vۭ+ \q2,N{$>l:JߟK.uz=qJS|f纹~f+O1ꫯ1E;qVJ_F;6a6}HW .tXkȑUNEX-֟|I*x 6 tz5(?>F/s=R(9U"(Za?#R>*>ʆnĈReeeɹ瞛.ՉcTlĶb - M- /.uS;秹2dH:abNXh|B} %ǣ /L(i03k1[6V?pEuůMP#~%O?tz;͈cT|q>'H{,K3Ψu[)4SS7+0iZ9wY@)J1BIvҩ1KӹsZ=}CzEt/;#r4lU5,F"(F{bĸCΛ4}1k6Wfg/NsD^y5硇J t:&F3s9cdNXfJ},8/2馛?0gGX -0\s()GqM4zk&LH@Qs(3>qԦXogNzi*nj%Ck"ќ5W1) 80AB| #HmGI>߃"(͖4g&b]21raOsӘ#y+F[o(hQ$ }ZT~*. Ջ7>W^ye:5-O;y/#0mPЂem Q;[c??-bXk (e]6]ScMS׶mjӘ u|䣏>:]jFL!K]g()cJYs K6]r($?䠃ticBlfX N7t…8'?)+:vXj q>bNY: 3S6Dc^#<6g_؟PjL!]ǎlpy%oqA}kĉ-Ph G3zd}MU_O;x(}Qśzj:a|֣ aVQc5(|f {Nyz~ 6(nb=^}R1E#)in.Ph1]lFIi̴[n)vBQ>b)'LGf)c9W\1֭[馛&[.vX*1,Y h4@)SX Lb=(0zP`@)SX Lb=(0zP`QT=XW_@%F]&L3QToVL@)֣ :wyPn@sX8p)]GO?XVV֪UVe;vgϞvnP*Qp Lb=(0zP`@)SX Lb=(0zP`@)SX Lb=(0zP`@)SX Lb=(0zP`@)SX Lb=(0zP`@)SX Lb= w:jԨ~iDzVnPZ*ر={յkwRX9S4bh| 8#'|R4w(ÇUӾtQ߾}-͙b= bܸqijX LE&K/tTb[oUfLQToqr!@% X(SX Lb=(0zP`@)SX Lb=(0zP`@)SX L@5矤G|n:yE]*I~{rV~wm9W^y䧟~J>#:9#@SO%K` k-JP䬳Κj{6mg=]:uꔬZk,Eh%P&NitM/~s9QXV]ud 6HT.汣4lZ>Y@ -J^OVLCz*7 ZkU{8NLas%vZm]vI TcbaÆU{+C|FG8'^x ce]~FꫯNÇ!?kxcDX3wziJT Q D1mYZ+BV{W+PIyKI%]|Ys'ݺul΅ =#;'uEj'oFZI"Y;*ɴiVZ)mkժU2?3GV:ҥKr'D$Xn{/& %Qh^ė{:+W^z{K?4H"@ya/"6;ViTYl,ֹc뮻3)_ LbONT8p`z;> ?k~eu1宓ޣr|UcPxzt;⭬^]]>]tѴ VTJ9/jufU9_BGqi`"&-j-L[FֲD8Ve7V\qkpL!](zNyzF{2>:b5\qiث"+i+Rs=SREh'sz~ %N;TX;H~!]yU ^)湮#O^8FIѢ8 Q1r?K~>. qeu1k޼yέ @&,pJ/jBÆ ^;y7hkW9O~}+t,URr_O?=jL,5D ڵk( 7pC'Uj}Z.9 嬰 1cm>?J[QI'Ћ^TYveų~WY0"YgJz / y&H2UW]7m[ZT+Y)*8-2euby}? :~Fk|ϪcY@]!=R.-@&,p?G+DTcNT*8ռK"xSSwy)S},ZFu ĸf gDU;&GydUw]tꩧνڶm[h"2Nv樎7޸Zd)mtLɶޭ`\ƍK`PeiQ%qw1mMWBy)?JjԨ? T^^`Ǻ_Vy饗޹s2#yGݻ'niZ ڗR-8jo)8弣mnI5UI1}/bsos9r<,۷OF=w}ڴiɻロӸKf̘Kv|@*澳Ė^z>j+w|/]Ϧ߾Ǎ"L]UE|D[ø^#dsuץC\G]vY+{y}?L]?_~(v(FJƎ;w=e"$_F"j|Z,uq!yGuD5-nyt/smS#('<ϵ2M6M6`t9߫WOoG+з~U[wfwzt7_p 殟yxJ|cqmr )L]ֲesI_pI'%[nez&L\xsFʮbDa~|x1駟ʿ9[ougCXX`DPW^94hPcKVYTv̙3; UG#*vz?s)Y;y'zhK𫯾p6yÇOsLVZiyousQQt˵ںyq! Ɉ#Vp^\!ZtAymVsǎ~^zir'V*L]?s++ٳ1x`WbףW\9QoU?Cm\]x&~>Rʉסt+lugXVn.Du<jl@6jǷ5~K/C=jz#H[>"-,4&]vM۵Fk^82eJZ.~az_T^duI5kVŘsM(ļj4\뮻&Kꫯ~!6,iѢE2k֬oMxGMČ/g R}_|Iv2Q}g}I+Zi8^mԩS5Xc +$]tI6d:[r%*nywr=ؼŵQq2gBnҖ{{+:\Eh_~y+b^68ŵǺjA +'MXoy>ޡCjFmT龢XnRA/M<RZT;SZeնm_56Y> O>$}-y~ym{iͽ]z99WRn5+LM;ÒgyqQ09ԩSXh^3zsV=?K:K^( %N /V!Ly?wcOrYm>_)!>?_f |3յ^{Ky:^Te^z5PRTBk G bv;wN[l 7tH7/Xb?}^-"^ulcW[o]缦yy>5}n_^YjQV}gx*V*a=2DK7Z;?IV^ybO sg_P۸>꾨.7CT|ǒyV@ʱ 'J뮻&~&>hk{j'VGI<#jG7rȴMvr-o3JX 4HzU +$FdO@Y\u_Julz1a=ZB$dLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX4hPτ czdaÆ3=0{ @!rΚ>}zbN&Mǿ{hٲS6۷^ziٳg/S~ٽ{ /=NX?]tE'RdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz@Q4j|͚5As EѸq>}zYI&7cƌY&B -k>-\ kz@Qʓ7qVY&*2!q&LX%ۙ@hժ|Ʃ@X(ƍXm>j1cF~. EӡC* 녗^zuP4n˷~~{7=î/Ĝ z@De|=3[g=ȒP4l+K-Է[q}cǎm$a=vmnC*7|=}'wyYg (Ĝ EեKG5k6w*[o_w/jnPSjР?|_~|ec SXHX(={'7lذW]u1͛7":[n9w[V4n֬Y ;/S 57 z@pi_YX/\|}N= ]tџ 1/ z@;<ٹs׹q3gl8`"WjvmSٸK.#85\s|!JX5fwyG~sec?@XUz+ٳW4nԨQ[|B KXU[n/=3N;+{!ms#W@u Ωzaz7ME^.]}뭷R@uOd'|r^{{os| 둉[n@A=ſEbυ?6mڌ{֮llT6lXB !G&&LJ[oZW=z {-:QF}km|*s9g>[9rbPی;]1bv 'Onٸq/;vݭ[~{L6B -kELj ~a뚞K/ͷ~T܎gqnM IXj׮]{@yy{ 4Fm1c6ls=y&MGh/Ic]S2_?:ڬE⋛}~Z#Eo<$[veDئMN3gNt_󳧤3g6rn}СvasNTE&{l+֣V__}c=ʫo:ufmO<",B̳.6åFX\, s={]}ս~ŏ>kbyGvҥˣŚ3gMڵk76*P;S/tt-k>h#8no.& uҕW^y[n9r;QmlEs/Rf=AÆ gƹʄ\z饽]wwƍ&mJm(x Zl99'Lb-cEE ?f`ꬽ_~e:4vvlw֯՛G_fͦf=EV&zQy/*U~矛GnmZD00*&<9n;;cw}w5\s|߾}/KN蘗]v ҵFݺu?^c>CoXfewes)ݻw# >hʂzEz뭟hLT<3έn'x% ` 5:[Ѹnʭ =c%&qqc7‡~X{<^@uZz뭿D}JqÇ#ҏEk=g[ 4=N={(_wuGW\q p֎;Dl_,ֿ~\,ՙcrrآE"8m;ܷfjoݷ?sp _+I~r)=33ǚW_jۿm8Z!Ѫ95O8s{[na[n90Ǝ3f6mڌk'MN8뭷ޛǏ_k׮uwn^zѢo}=zv뭷ݳ6 Ql#ջwKe_>ۼd`kA!|F.N'卉Tɠ^ӨbXrLlb)YN;?>w!ҏErW^e]vJT+]uqĉ[E諪inrR[mճel~Tuya] 7Jcڶmvqmѫ\qt:]QAco q\[W67x!Q16lX&Rz?}QQFևzx'2cǎ+&&lrrQk.A.U sKV+vNTxvǣB",qX0`>`#y?sTyOӦMtA7_tE'E7?YguލJnK.wVZT +9t`t<NUJ+}ZmB9;ߊ*J鶷/Rbͥ6(V5kV_~Y8qf:`#iѺc*D[ը׷o Zh_Ajg|/nS=n)۵k7}7MfaW(w/Xs*Ke ar]vywk>^߽Xú<[܎mMT IgϮ?W[EZh39s̆_|qzfMϽ<~.O NhK/w3ܝw޹oÍ*'LJeaʊ۹׸8餓.F"g=ꈪ}U1cFb )S,X%ߤI=o.m8qb}+k?׍6ոok|"Wz|F^ʵ0=ЮY=5uUWaxÆ Q~%F,>[rYxEQc?N;gE૬"!űcǶ}z:k9sf+hO-t9~5+z_D^B u /)AvڵYi~YdE E@|ZtEz7؋Q>JK,+<)}=zVZm-ezw߽wT_^q? qwEV,mŲ6<sQ0sW_ +yǎGGkXffmbgg1bv_|ӦMkZϪz`,b9>{챝^zQ0˜͛79^XV]uO"`[=VZMsҤI+#AXmqa=N PqƵ`U曥 uR_x"\'pe7&Bpԩ 5=yKXk2dHlR)KÆ gFĚxKU`7BQ%p^@;SnTgRK-ZTtz^̟_n=餓.mo;蠃nέ_{G5idzaf 5z@H7tӗEEB̍S袋N% ֤YW6a=֚={v 6`L*ǯ)Ǽ:î\lذa=}.Qi/Z7klj-l^ڵ{.OX=&Nتq՛w_Lb-c^s>a=V馛g߿k^SjjAts>c=+isy!'?CzwI^;!c?fjPٸuY+B 慰E1eʔN|JǮM7ݴ3|3v{d= zo?$\p5m4Yjy睗̚5+K 9U+9m4h0vش*ߋ/L<9駟%X"Y~Ν;'[W93&袋/"ɶnx/ {ɗ_~_9cF}ѣG'v[z:w֭w9s= kWq%Æ K+Ɯc-[LVYedmIv,B_M}駟N}oIfϞ,_dwLvi~՚}ݗ'&&M$.lzkݾu]w}u]O<|gԩSj:uJ;dUWge pV>cI}qVs"@Aso/iȭ&E܃:( _%oW_޷;$W]uU,˚k’K.r"t.쒆KS'hBf͒C=4GiSLI^x!90wߝ yQF%?o?>}zˈ#Xrےb *~i<#z"ZkU89s$|pϗ̙3?8]"A8wo ݻwW^)KuW2TsPZwyg4n8 U~..ovƭjͅa=2alV*Ec axWڄ  u?C\k׮]Ϧ*뮻&g"v9礷#WRСC/2q9[Tϋ\jnQJtqbK+UUվ⋴ԫWq/RZ81iӦ+Rr7'Z*s?pXIѶ&E(hj۶m;6OJYT>`LjYI&6 Q]h ><0ׯ_Ex/Z'J[췲jrExK-Uq:h!Yg} .yz 7L6|M6e/yXŠ+\s5)ľP:NH92V7xc[.{GT졇? +[!5IX:駟C{ڨVA\wy'y^mYf?G[|VY!27oYIn*ݮ}V+OT 駟vN8ǧQ^T;7pC'UK,] u]DeDp1Wǵ>J U5x#׳g!Y @JW+]io^DҒZnv}JmԨ6lXzEaW*ꪫa}ҤInSo\mrra[Z)s=7!XJףcǎɑGYfBﻦ)ni%3Ϛ5+@&OGRHa=.a=29^zoIן}dTW-:Qz\e-QU"T:Xگ Ҵ[l#\8rj"۷OF=w}ڴiɻロ=\2cƌXBq4?\Pe]nѢWO2!@Ar)I>}J/NN>y&lgy&` *.DHn 7̋m6Ʌr"YHX{Q?NV[m̏۴i5{^<}1$ov y1\|z#@AD?0dȐO.Js^zirI'鱨ֽ{;Hׯs ;tw'>?>iܸqURݻw2jԨdE-.9oVFmT"L=3=+RA]s9spmw>1ufO=.`"l:(m;]ڵkI-Z"'w_:6 {n2s4"ԲeNHbd%H'L䧟~mZ1E;h_:vVrk۶mرc9sG`n(\"b^ Hc:>V[kaSLI'O=TXc!wXa.]uuI\rɤ^zQgr8p`mwVu 9r䖳gϮ_ٸΝ;?RK}[9@(ۧ-E?Ӥ_~itEg]~#PugC=L<9۷oc#x~5j4/OFDm̘1#5z2E.BY[. (–<y4iRr-KMСCӥ<:r!i{T}vah=[ KYuԩu z饗:3ߛ_z.%a="Zx5͛' J0~rkҤIGxT"XjRT`<"*RS5k\__,-c7xƎ%tM_|^z@8qzuz@Q|KRٸ%X_B "ń Vg*2!ۙ@Zj51@ք2eʲ[q?z.5a=(O$qM4\ kz@QkܸYbƌ'@X(Yf5g\gg=ȚdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ([n%y衇= }0֣t 둉7xcb( P9a=ȘX>|Şb֣ڵkŞPwy';vl0_֣vc-42\yz5DX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX2& TA~!ݻwW^ŝu?󴏗^z)YfejhF'f͚s?[n5~^{ OX:sIobOZw%wuWO$}Y2uԤEIN;,YuUc&O=T2z/LaÆRK-,iE78j_7n\2lذ_NӨQe˖*l6I׮]Z)qn|K,n:y睓=3=7 x=P/&'ON~\,IΝv-Y}Y^o J{O>ywߝt=9s~ɝwޙ.7NO+r^yW={&?CO>=_I&%zz.2dH5jT}O>~M7T1>t1bD*W_\z^nz~{W2f̘v6mYiN.vf͒C=4+4nʔ) /$vZ˦ +P汣bYϷO,VQ.UsݻwҫW ǔA4ܕX| ;CrUW% 4(wa=x{w:묳دV7裏3f"פIr7gΜd=HD}i:/"9c?>W^^z饴z[I4>ٳ=z$*=fra{E5DP0zq s?ꨣPabKv?3Lf͚V(9nݺU:l^Z `V&*FuøFBT<]Y'LVcE]vɳ>VGM_a=ꄲYv%UNH927xc[8{G0^y8∹AZӥ +ԭHʼ?Eկ* E5'BoO[lz&+EU>}$ÇO0aT|zm۶MxrF/^ F䨒|0cu N$@VW\1Yx?/w￟VˉYЪ+*UԫH jT ]wݕĢZiQ:u ne9䐹a~-m;/j.L6˵~w{/Y{+ݶ&k杰 ZVek[-χ~[o[k>Ӽz5y]0-X1iMJzoFr-$gNz葌1b^{m /#6t*?E ,U$4m޼sʔ"؜(a=A;vLCY?s~w$m]g~%zo'|Ir'w^o&_|qÆ K7n\92fm*sa=*6d??3lPv1.'na @ 0_z>+꫋޿du֩־QT0K9[mUF8-X?&L-w8C\Jt;y IP|zP"`O'k/$?x'kvrA%mYҢE%~}GӖQunȐ!ET馛nݺ'|Ǻt钶ǭ-]\T Ɖ'Ϩ =D .Be9~<^zi[nI:u9塇J&O۷}D/6بQJ@%@.Kst~WګjuD>FYVҭޚEÆ ӰZ`Mqe^\54E>KB^T'*Y-ڷF;zo|i#F̽CMw^ę@' >;mq[ڲ.mӦMfa=XEX/,y :a=ȘdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX4hPτ czdaÆ3=0{ @!rΚ>}zbN&Mǿ{hٲS6۷^ziٳg/S~ٽ{ /=NX?]tE'RdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX̽ >|I&駟#Kt>|x+_~I:4 Uyo ;CrUW% 4(ss9'馛=NK<䓕j\WGmF8Cf͒C=)S^H}dVpY\#9sI>/3g&qD5'l\+ۯ1{NzUᘜB\YQF%0 )M>=eĈgNmc^ziz;B[.y^sg6m`"@I~R[z饿z.5a=x7x֣Uy&LV,6wV7|sҪU2?3G|I.]O>9ٳgu]] Q^l:th2pvjJqVTʊTY*}e=?v%>lҢE?=3ӥF0OuV-O!uj5aq"X ]T;ꨣUbK+4%k$'_^{챴bdy"N>6t䣏>۷OXUY_YDX/H+pFz9^J+A7x#V=VЕϣ\Pqi5?Ҿ曥RK}\ kz,P" ?'|AZ)**EŪ^m :#LU>\p/yFhs}oV*OO.^czrm&<@ǎ+"<K| gM+Թ.K]Fׯ"V[ 룬JuY_#%8?Š+\s5~J}Uʼ?hU%#D+ TF/W0\i;i҄X0_߿'B0Qq*W V` aO?=9DEXƏGuX,֣-nG{͘c&MUnTpL"k;$^kWmM)Թu 2rܰoKW+5 7ܐ|'iZ6ϊ~۷oAqw#lx]wL>w=*uԩoƍ+Uߺu dMXZT d9?c'rK\zeVKCM͛7/w_ UzW9E[ҊD(Fat`zɰ^-uyͱO?aBP&\eV]u?O4Oc q{{Eh-رcrG[.+q7D+K.$ݻw#ײe?6Ѫ9D5;n ̟~mI%\?k&W6j;a=(цq 6H.,NϦ_?֔mA#GjV:X TږִbpDh[:YZT8+Fڷo=ziӒw}7y7{dƌ韱h;5DžZ۔%*iuI=#F}kM^x/V(OUڴi3.@!@'tRr}#rw'nFh{IoGoElM63<*r"VRm:׵ `H.UJ+5Ҵiӹh3ӫWOos1o=Rd]WWߏE5_=}mq{>_|Euݻ'wqG~Wm)KJ ZM\ &Lּk۹H"5jT袋w}rjd6ӸB_#^#Ky.k۹v[ڪ87yiϟxuY3ꂧ~af<(k'C Iu떼zydø@E}'#EKƏ}ip6m6]#OFU;ryc:sG,*'k$ 5 I.]K&K>ofou]cVM7ݔ''V;"<^zi%^N:U ~RTj;mq[ڲ.#nӦMfUW]u1޽Y IX8#!z̫ǯ裏vgQGumB bРAg\=-S2of!Cg?AO NX\߾}/={vmOoB IX￿֍7xH>cxJb2իW׭[6p׳n?3 /|XL͏:k{9٪UY EX^{uϴiӚV6n5g[9@5n<;3vذa=PFSO \poPc&Nj-؝vSN9e`s@X?Ӣ뭷ޛ3gά%\oz<矛*ۨQ|6m:s@X'~RQY/&+gY ja={オ_7O$#F.g<-ujٳ|ǿ⋛u,P%3glvۍ9roРz/mڴvwݭ[̙S//ƍkb6(ǠAH{loM6iΏ<ûd9/+2M4i޽{_zYի7߽k׮f57kNÆ g{c?]Uwm'|r ̪y@]',p9 pӛ{.&ML߱=UVy晭W]uO=N˖-'O:Y߾}/K{Ϟ=~Pկ_vH bχX}?z駷Yy'{.P  2.:)bFvꩧ^pg2GXP fmOx≗lv#={0 @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX2& @Ƅ cz1a=ȘdLX2&u߯ǚ5k6cǎjgnl+՛S9@] ,>5w;_ =d 7|;w5sxNlĈrg[N:p_ *h뮻;[bK͘1qB /t%[KNh^-X̙3.EU5j7q;*2|mf/{^P(!z矻{Q3gNbσ>Xcǎv= :MN+ Kz@_~Yx~m瞽>azҾ}{챝YfyP U ?\pj)S,{epW믿.T}k[h[oL4i2g#Ԉe]vO%q}+廏gyfh{5}QG]lL}|ҤI+Ϟ=駟~^.ė>kרQ߲'ώv 2G98Ǝn%[{W,y@ٳX8[ ֣k}/#dAX([o?|V[m9sU4vڴiM;tĉ[իWoN5IX(-b\{ߛ5kV~+p 4(a=hZnڵ냕k8p)M6V@Mj]w}h7wW4nƌhjPc'{オ 1iz@5idz>㢺^s,@Ƅ cz1a=ȘdLX2& @ƄCÆ ۶gϞȭ{6mtF@ @Ƅ czy:uj;wܱuecW]u]v}.iȑO)&MvM7]T}P1a=JD[_c5>?ݩS[lٳM2eW^yex?iO7o>X.CW|+/[oիkuYO(>s 7\Z@Y*qg}>0pKYeUIJ>-.~kw-畼73[?ꨣέFm4* O~׋[Ff}4xAz%p#zulT^zia=#Pxh#?/;גּ[n6|۷o?~x 6*iwIoaE׫WoO?-?=&PUW]u)S:ujh{=lꫯ_.{XJos)ygV}԰aU@SfͦyRz3<[ouX8p#Fpȑ'{@ Xfe~8ngw޹o,=z 6Es938,7,2|[k56:c2>gΜz<>O &:r-GnᆯG8'm #V[ĶYs~AݻQј͛ktM_ono۶ۅ#P͜9a;g͚ՠq͚5z%xG.`A矯p衇OXxOtA7x≗b~ 2a=VZi>믗vj۷-n:^x}wuׇ/ZgOosh^xS֭?,|CP]ve'z{:[veg߾_|vL:QGuG}߿{׮],)[/5j[n}Сvaq|լRza};vݫW;w|˖-''zGzk]{GK9B(Ե Pa=V駟~>oUw]ddu}'*ľVXak~u_|a_Qū;䯿W_}"Z2Gq]! =m\~-ަMqݺu+yT/k/jbe]9\WDe믿O0a&!~5mݫ>>k7\պhsw߽w."8q]|}Y{[n9_죏>Z=~4mtZ6x?Ώ&d9#[O(n<~}Gn ʉmϓ,+P}z@_n^}սby6 yԨ}wKR8Ny5qA#85z莱>}&~?םvzT="`Cr_s7|PT˭xpkIvk{yoq% /ˉ'xI\Ex-#Flw@O|>E躲du]g_+ {m ǘSRX{<\Q :Ͼ)Wn=ZoVFcs%ڞ6idzW_}*qDH_c"%7;^{uOUϼ(Ίs-qkb߅|ϒSOm_FXu 4o粶)ĵjs$~?^^fei_~e%}KnKVV4o8:i7. [T+fM/ c ˛#UB~ҤI+G`իWoNYy晭#4W}BQ*Qaبxa6[n/K*L*D;ռrA1 ")nʉ/c~<cq0@)+\V8qGX'1Z5G9*|7K/y+ַo =B9EwkUo" D;ʨhǏfeǏ-b@jU,%#v~~ !-gTq΢j<[VYe x ue/"f#[XϾ{oDU>K XJ #va]_'[BV}E%ΨZV@+ύ8kWỨүY[  XӨ6/= 0ԋ رcE%G0-彇Bv֟#ݻwsFP/}'tEq;•1"NJpy"\#KOr W*ViC 2^T?ٳ琬SD5XSY{D*/KVQ#P2V9͋C> sq>ғ "K/w>sVmGa,]Hq]U6&[>`b~UnϹ~DEUzlT MnFs=׹ތ3qKW̼|UGTϹhֵP.LUxjN'*k\骪ka$s/~~s={E{nG,qRTr+oY>E0q;ޓQ/*PV);E-^BpW9(a=Έ IQUD9*4oBEW\[Urˑ?vmrV~퟊Lt,>s%p4nxF*W裏V/=&2ٗl{TQv^_Sݱd UbK[nS^P~,(xF8~nE{~QtT+oY~> <8}:~Rz\tT+e!st}Nz@U"m骻3BfQusϗsuG8RVED.P*bt]wuv%<9DӪ=Syw\ɖ(dVUYλbNn1B^l|T몫:&vmS .85n'U[l1G9oO7ƕ\OeDZYHY!,g_k駟&A0ڻQssréXɟ3SE9e]vB5r}mE!,?:uBZ|vFؗ_~yhO0}&g,1&ZF`:`^sۄZ+vn>hl_(ǗQ%FfXv%CEfرFŃ>vp|kh#X5Qahذa=v6PNh|[F_ V@FMUrޅ%#\ј\$'{e+ k>AqEk8Ovq*Ms!DUn EŭxT]ݎ~>NjsU+S\R|&z;BM.씊`S 3Gl}>Eh![YUĸ> 3FZro]m B }c9/ݿ^{!{!B#@ RTvi嵨+O׮][n;2j`~3=3Z*_ms7rgA#׆/Zu E+vxZm _4.D r"Pp '\Ͼ#xOz&^ Wmw!Eߕn Ah[4Zl,r_N:СCi>}.=~WZoޜgQ=-[|9yg_ܧK0=TV-/'ZoF(ʄQ5BC.]<TKR\Kq*9MRYzT˵,rg\w{K.9qرb=Y:}E_Yb>?RRK-m>DN(U׿>϶|^<|?CBM]us(>a=9S/8p) CQq弨=|dQ"Q=+ 0DP/\n{߿?xn~sh!V_}"j_qe(ڊif\mҤI+G"*Ep]vc# /t:3ωxg7|A۷-yՎ"dTzW7zꩧv]ww~wDk֨n~f{̘1wy?;n}gwQ1Uc7}蓩x/E5?y,x|g+FX?-1^zs#`q!h.㱸_ܖn[ZuV ˏ;|;szy3fLhcl9ϧ!5kY?~_mfg},~of}oc ޮ#@ U~w\YD0%xU}%(sE͍@gׯ_|1" llP/5j7()<E _aɓ'+#0D*E{?So1bĈ9眫"k%}WE0[Qᱨ9mфaÆ$U"طo߳ V\J?*ӧOrQ3ϧAD%9?E8&~f[jqikNKr޽ٳg׿{QxnT݋~_K֋"娞AG00Eݖg_"6~M"U?oQO ~>obo{1w"P^Z<|x}4>":c7'y|Dஸj%vn#@ UFkӦ{v^H{_KC9dX'*D՟^7*D( R,G;݊OI"8A]bg%K{OuQ,FZYs=Ky+I-3cD(*FE: V1+o͒>VE4?:!1sVb뭷~`P8cǎݼqTY+T:QYOQͮ?+羢֋jyK:OHtz@O뤲=STi^+FF =sϽ*{9-I%@ U™gymYwqټyD^z~G^ߣG~'x͹,K|kܹC:u7n<=ڧFܻh#p¼φn4/ |>9z@΍3}oiVYen+cO-4{Zkhž`YեK>uui-7t.ge|z@Ε%\8p1r"ڊl- m嗟ջw^1r\P1o(˼W_۴i7k֬͛W'ԩ3/zՉLk߾_~y\:q_3fL\:i@u@ PFE_|š@UԤI{-ZT# iذ\LXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @m6駟2{t=:tN<ӳ}zͫ}?M6ԩӫDX zǒN;k֬4h Yy啓VZ%[mU& 6MPfzϙ1u'LzmKnN:9%yw*tx_lrW^|zq~=$_|qkl?:EJ 8ÒVZiqܨvW' (gϞ< /|w},\0iԨQf%{{$yyyKFpܸqɌ32-|[h^IΝZ+)5jT'ӧOO~̚kV;$_/^?dĈ{w|gɯԭ[7Yc56mdN;Gu.JCM|믿Nj׮4n8Ywuw9~xcǎm~c)S}g\sz w^ر[Uï4dȐymî1k֬z~w 6˲5s]kI&fuXv&.>ezK͛o->߿g# 7ܰtMI߾}3[nc-247dƫ Ó^صg͚y䑙]Q"cرɠA2n5knXhQrQG%J/X 2eJfdC[6h`N._cǎWzoOǸ;vpo=.n[^W̜9^qkw};^zgYXJ~8Jgř:ujݜ9o.;L5kV_|191s=s9'9KS"•z?L*{۷Ono֢ZBTX\Æ |~Q履~Zn=:Y}ՋkAy= +P/"+K=a뭷N&O?׷m6%qEeFAQQe˖c|.J%SNTQFxLu"gCk׮VХ_6WNL5?`ّw6LפIz뭓W\qYV?-{Je]PwO:Q}mn'Ndܹvz+2{|'?ҧO7D=ʂs(a=j(T3gL>LvA͢] -Jt]T* tPNjQ]/Zh;f#HzUm޻曓:+9rd]S.*ecni#, E0q<'3+OIAФI_WswQJ_ꪫ8om)xGKި*¨*XR2Bيav#f/ ͚5{-s%]E]tϹ;S}{o-bވ#v8C_(8/ _ڳgZ3~/Y:ugi[,˄޽{gFq"$U"WVz^ ʴcĉ{Lah[c9fqXobpQ-~X|UJe[?>0aBF9?w}˷v[TxlJ}MeٳԠ^i{6lX2c FQU&gϞ񋏣oď TO9_1.p w}m۶*]БGLIGAgΜY?M?叇Kݻj駟>BX|L~dʔ)]wݕ 曙ѼyLAŮ emKh[[ڵkg*Еfip-g jѢEv>bc_~yҩSL@/[b.֭[ު8Er-<8kMz9kܸEhߩ~nA]ty?믿^'2Fyj-l߾^W^yfisf/yo:uk=15[lEis#a=j(Zui&3LѣGg|_~8ӺfE^JmQ%/F$Q嬠-p !WZk۶m&9s$q\/'#D+w߽L.*|V~r/NT{3aօ &]vM{ŷxɫܰaLI`ٵ~"ah[l-+*V[_K"0X(Iz"pVݓz*sON>än2f]ロymq{+2kfC&u>~'FGva=];cƌ/Ϋ*k:t`ذa;_~e~ev&t-pOX`)ѷod={_{XTtoN>G(O6p?̋bK3E:(Sy/Gn֬Y7|7.yWg}6t 6XV+I^^^f~Ten2Uycn!y7Ϗ0XT ,_,X  &kܸqrg$۷OV^yo uyeEp,' 5ɞ{KVYeFɗ_~i5|M7%}^uULvX5o['&'tRn/\4wevC Ls9gmG{\`5szkFNӦM;8.{᧍5%;iӦOk0{oui7xԶmO?m矯ְaÑv#7dͫ믷o|A:u,ȿ+TXD=#]drO??n<̈"p6mڴ䮻ʌ{/ 3^{yW-J\/k5jT2}gϞE΍ݡWv29Byzkf'>c:Ԫzw_~QxM~Rgyf&FE(J_bת󝶨xfK֪?w]b7'tѺwwWq׮Hu֝}gE`oȑ(8///o%\rw6mzO. P] TA$ \YeE%i0[ A [EŹɓ'Wz <8uKRi;+*u *}U"s̚5kv׮]Q|ׇuvmф )ܴ#JXXj}  ,UϟL4)S [|t%;`Y&,.LXcL;78#@֭[f@e^ ez2a=HLXR&) 9믿Py+¯i O?{YmF AXȩoqƵ.m^Æ }ݟ=@Er[nn*ܳ>o^^´irb'r)2wm}Ͽ2=@ZJ5k֬#|Wַa_)}@J</}[oOǏIݺu禹7H3fv_xpÇ?0}@e*kׯE]tY\.}kժ };+i*P!}] ^z饿.g}v߫Tz@͞=|YT{6z ,pz7f͚W>uI޽{Jc:u̻O֭MiU@-ˏ>`ivA=0hР5.{",jժ`s(KzTQA/E @.˴or! {w^-aÆ}P5?p z7Z_z?P˴ 6wymMݺunc׿N;;ܠATUz@.\;a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HPqsmРTWz2i…y={ӯ_q9ѣ_>}z\t\ݻWIs6m:m]vyz Y?.eS:HPp5ל5`SNnƍzOƴiӚ~^}gԙgy;r.Ủ@8*cWz/T|GQmWZis?lz2g[Ei~7|1N%Z֫WoN_[n9!FF5j>lIX~zr)z95`9tuۧ~{;vKw}hm}N: vHNF @%,[s9&N#/ZͫXq=zt6mڼW;*L˖-'>Co\ pʢEjˊ[lŻ{7//oa;!b?-ƸqZOkO<Ğ+¯ǏduםV uz@Zn=Zkʔ)waWڋ|j'ܣcǎOU> -z@i޼ 6.]WO;uatCn=FM1cj͏J?W 9*0mڴ6x%͝3gNN8T$a= gի7^y7ԩ|J;zB2IXȹ6샲&MԢ2MXȹep¼i @ʄ ez2a=HLXR&) RJzx9 , ez2a=H,c.\\uUmVf͚%7pC馛i~-袋Gy$YhQj֬yɹ瞛\VcƌI>3>,o6(yGK ]pɰa7n893w1Yy3!GE_~In̈ ?%zoZ6m]&[ouꪫf~a&=O{dĉa2{dM5j$cǎM E8^͛''tR[&z&9cƌK~g<&.r%}=$_| $sLұcLx3֞(}3xԭ[gI^:u$ ʄ]wݕYcm-zAt>,cjժ꫙@TAQ,[,v <0H*J ?T Q-*ň9眓kuV:uYsE.F w}5j=:_l_&;SfEp,ϳ;\`fZl?ZnQ.^M4)u]31ǒuQɐ!C2'|r&㏙Y6mZ\`&˘h[8W + OEUB[ ["0U8WXΝ3AhmԮ]\!**M#  Z{3!YMSy\`APY6"WT0޽{'FTKB6=r=!v/b O?i\ZݻwTU ٰ_Qry>,cڷo_ꜨfVPT-+֋ СC?ez6{wm٦L:5?+nYt-(>Qe;+5kV'p 3ׯoyqT{+m֓O>!Ez9+(גYz6"8~e߯6nz)K^z .o3g̙^yn\sr'/_Ĺ*|̞=;>|x=kT>o2Ub8b׌teϕW쩼uZ3ZdѣG_89F|K7a=XLo_||M7%g}vVZuU>}ds9^"v-dZG{믿>9*{Jzsxg2-q;u 0 裏2s5j_ITMz k3j5kmQ,*=裋`jVZC Iog|=P&WXT_hڵܫ -ZH.qMj~2m VT{7+uʼnq$Jq;C&DYӧgemfEŠ+<{̟??24k֬=͙3'y3 dO4l0kO?=sqZk~/p>IX1d c$J$뭷^s6tLնş}YҮ]RСCLx*{Ta*{K2jԨ(ο䤓N7Ȍm2ϡ$b0aBrueF^lP=a}ɴÍi-=ŷu=iӦMZP2&ňСC3!3fdVQnݺeZAj}!֊믿6sdVT9jp3A\{wV7lذ'T={vҸqd-L$o3mcDո8q~n]{gqFfDg}6xqLqʪI&m=u13*RZcVZ2aƊ-z2'?>92~KwYg->˒:uTX˜h{뭷fFYD{7dMRK+a=`sgƯ<ѣ>('jҦMd]vIvm$///[Y+BrgIXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&P>>}zSǏ z7n\f%)9뮻\`i;@ɄeGqɓ׿K/^_|+ʄeR޽{[9ӴiӚ.ZFPԨQcQӦMuidV>~X:ԪUkAY-\0/@eZ`k޼̋&@a^)˼x+-ZLJ{OPkҤ-mN;ɓׯSμTa=J2dQ7>o޼:%/:O0aW^z@QF?]ovUVY凡Cv=!T3<~)]=s~W_wu=Xbz@>:qĖms̩Wj[ӵZ뫧zc֭U>Vo֖Æ ;_qڴiMU[nߚ4iŖ[nV*5Z~gzihԨwq. /<{z@|2k֬c'yrEFN>W]uչ2K{KqƵy_u?Ro2dȐooTFY;v/g~`e=~Ytr_ySnݹg}vߋ/ڵkOkoT] ,U[?rO@nUR^^§~zw}w:_W(-ZTcĈ}gT6xqtOGK/=5;KH?|vڽ=8pɕ+J|D88{?Qwi#F?wܺ_~1bn2%Ti[lŻˊ=PC=y)GOFm4!*cuM6d|xWv({:t5k~#駟kT(.ݻ V[mFǎE`;.G0vѣ_nw @.BBk~ȒV>0aFzyן!+-ZLZ]Wm}]wݩi=gy_~vIڴi=sXV>.O=Th=[lrbϞ=\{gF{uם㽵k|~V@#<_\@h졤U|*Ct2f'xsT|ٰ^xG-KX#Qa4[s嗟N;Au/>[(o̙ {)=VVrʀ\ں~[[n7}݇6yS暳:u4GN;XҜ.좨8Z}Jrg6>GJZ#]Q!1/~N2y.=쳻eᦛn1{+#_@U$T+mk3fXmΜ9"hr-̞=~T|2~Mj׮=?ʕv홂ny뭷\s5ǸqI'+{+rg\qDPu]G{M"-ի7lhmy#*}iW6.wqaާnݺs|vug8*y_(-p6 *"az8Pdxr6mZӂǥ|U{$>=ko{ .m۶movfܸqc ;^( ~7iTOWkH޽{EtŸ+/v8∻n?+۷o?&… O_6#"٠A(]% v/M6{nO6(ԋKa=Z@7|bh!l̊9VJzo;Q,*xZ_m+¯@[of$Z)|m*tM?,<]voǸNR"S^^;EsϽF5-ׯG9~h^.CjyʊπK?Gn^>E3!;%'Ja/^lp:r!âiy UPEKhV[;ZFQDe׎TQ>oFF}'̆BU,*tTXD5>YR-sxEJ"B6?^&MjayG7?Xـh6WԼz>r)’/_rT{Wv 1J:|>x W,#CZ8WPTMv:4RoMx f/  ;ꨣDh_|N:-{~U9-8E!Ѻ>ъ9s'b/j1s{<׸ܬYϪbPLD.(娼*pڀN)=gTiHTȋ*u{mQ]*=meik]]}#tUa~畊ۼy)8юo?&ꫯZ+* (|8w饗^\T'qRQ!O>W|ּۿ&Q 0{^E<6Tz@n^hyլYv/e %GU.\a>"m3 +kIUwYg]ƌ>O1bۣ^ّGyg?KZr7m:rTՊz5.Zy6x"x-n?uR\Q+=?}ZҼ?<.<ܪuJ{`NQ",Ȣ‘6wqq8Qu}<蠃(i]_s5ge#曏]'_Q0o~G}G\"+Bg%FU%Y?#oGnZ5# ø.*p j&a=J:yUW[DTマI\y0K<GG$ ~D+s6l6mڼ޽{jufϞ]С]2փ>x{KnUV;ڵ{;.[lUO> ^l:űW>3*gAy-j%._֮]{~yI|WkmҤ=Fh=@Z*%OE+-eOTJ{[ZEh9{%)%mF[hK9a„"XRLje.]c5*ehX@p>0qĖQ-{|-nv#$8!2,!U{d嗟uꩧ4w5:>kU"paf7W[m!*E57'~D?/T?CKUJ[+1-BeseS"ߖ|I;*Eȳ<ߕiȐ!GExgz!^ro&a=ʈ`F7n\̏gQQ(ZMQTsU#L4f̘Q?_'!QiWySSTҋZI 1chTUoֈ~ 4eE%;{e9^/%YA8Q-#r4D8,Kq䢊XT+<2#W^y1x=# ;ﴍ#tM? GygYgDr" /_/᫨J]ZYO?]/‚o-S"`ڋM|Wxgq^{x.i5߯_iiYꪫ~!l( jZйsbT;v|*:LC"TVQQXV>3gN8>^xᅗz_%jx1~QiG k1*jh[wu85CT̬*BtJ*| )Ftz@0pew}xT J{O@:~Q->`Sr/ }QI3*Fe8Jmu ՕP%ݻWYlA=ޢ})Sz@jAu[T|ڬ V vei3ڲeˉgu5'b-r5kN:<Ӯ_}տ2 9W [{ci`it^#`mczPիWUK^^=zӧOϸPO 2oV9@*X" .̻k0`)SN]qs'JXa5kX ̝;~ePU 2L ̛7/[[`o(<ej?>꫓n-ژzKTe\^^^rfmT{z2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ1cjejH{/a= ~`YmF AXȩs9ofhbN;be *3}=;FY^veH;w~hĈeav!2,=@ZJoll̙S,wug5}@J1a„"x_4)}<'p-SN]mѢŤwy+K [w}w~}uY`CgGo&+Z++|g&Lرc7=zt)S4Lj zFڧ}c*b=ȩgUS>}zݻWkkzsӦM>T%z@i֬g>n]vy.{$G*jժ {*$ׯ_֭[^ WH1s^zܹsz/@֭;7N0jXc59?ShbRUhܸ~m={[r…yPѣ_>}z\ѤI/6phӦ{:t-#5s}=;Fr ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&) @ʄ ez2a=HLXR&)?vIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/internals.rst0000644000076500000240000000320414716225054016202 0ustar00twstaff.. include:: global.rst.inc .. _internals: Internals ========= The internals chapter describes and analyses most of the inner workings of Borg. Borg uses a low-level, key-value store, the :ref:`repository`, and implements a more complex data structure on top of it, which is made up of the :ref:`manifest `, :ref:`archives `, :ref:`items ` and data :ref:`chunks`. Each repository can hold multiple :ref:`archives `, which represent individual backups that contain a full archive of the files specified when the backup was performed. Deduplication is performed globally across all data in the repository (multiple backups and even multiple hosts), both on data and file metadata, using :ref:`chunks` created by the chunker using the Buzhash_ algorithm ("buzhash" chunker) or a simpler fixed blocksize algorithm ("fixed" chunker). To perform the repository-wide deduplication, a hash of each chunk is checked against the :ref:`chunks cache `, which is a hash-table of all chunks that already exist. .. figure:: internals/structure.png :figwidth: 100% :width: 100% Layers in Borg. On the very top commands are implemented, using a data access layer provided by the Archive and Item classes. The "key" object provides both compression and authenticated encryption used by the data access layer. The "key" object represents the sole trust boundary in Borg. The lowest layer is the repository, either accessed directly (Repository) or remotely (RemoteRepository). .. toctree:: :caption: Internals contents internals/security internals/data-structures internals/frontends ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/introduction.rst0000644000076500000240000000035014716225054016723 0ustar00twstaffIntroduction ============ .. this shim is here to fix the structure in the PDF rendering. without this stub, the elements in the toctree of index.rst show up a level below the README file included .. include:: ../README.rst ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731802199.6236665 borgbackup-2.0.0b14/docs/man/0000755000076500000240000000000014716232130014216 5ustar00twstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-analyze.10000644000076500000240000000562214716225054016706 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-ANALYZE" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-analyze \- Analyze archives .SH SYNOPSIS .sp borg [common options] analyze [options] .SH DESCRIPTION .sp Analyze archives to find \(dqhot spots\(dq. .sp Borg analyze relies on the usual archive matching options to select the archives that should be considered for analysis (e.g. \fB\-a series_name\fP). Then it iterates over all matching archives, over all contained files and collects information about chunks stored in all directories it encountered. .sp It considers chunk IDs and their plaintext sizes (we don\(aqt have the compressed size in the repository easily available) and adds up added/removed chunks\(aq sizes per direct parent directory and outputs a list of \(dqdirectory: size\(dq. .sp You can use that list to find directories with a lot of \(dqactivity\(dq \- maybe some of these are temporary or cache directories you did forget to exclude. .sp To not have these unwanted directories in your backups, you could carefully exclude these in \fBborg create\fP (for future backups) or use \fBborg recreate\fP to re\-create existing archives without these. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS Archive filters .INDENT 0.0 .TP .BI \-a \ PATTERN\fR,\fB \ \-\-match\-archives \ PATTERN only consider archives matching all patterns. see \(dqborg help match\-archives\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .TP .BI \-\-oldest \ TIMESPAN consider archives between the oldest archive\(aqs timestamp and (oldest + TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newest \ TIMESPAN consider archives between the newest archive\(aqs timestamp and (newest \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-older \ TIMESPAN consider archives older than (now \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newer \ TIMESPAN consider archives newer than (now \- TIMESPAN), e.g. 7d or 12m. .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-benchmark-cpu.10000644000076500000240000000266314716225054017764 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-BENCHMARK-CPU" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-benchmark-cpu \- Benchmark CPU bound operations. .SH SYNOPSIS .sp borg [common options] benchmark cpu [options] .SH DESCRIPTION .sp This command benchmarks misc. CPU bound borg operations. .sp It creates input data in memory, runs the operation and then displays throughput. To reduce outside influence on the timings, please make sure to run this with: .INDENT 0.0 .IP \(bu 2 an otherwise as idle as possible machine .IP \(bu 2 enough free memory so there will be no slow down due to paging activity .UNINDENT .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-benchmark-crud.10000644000076500000240000000660014716225054020125 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-BENCHMARK-CRUD" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-benchmark-crud \- Benchmark Create, Read, Update, Delete for archives. .SH SYNOPSIS .sp borg [common options] benchmark crud [options] PATH .SH DESCRIPTION .sp This command benchmarks borg CRUD (create, read, update, delete) operations. .sp It creates input data below the given PATH and backups this data into the given REPO. The REPO must already exist (it could be a fresh empty repo or an existing repo, the command will create / read / update / delete some archives named borg\-benchmark\-crud* there. .sp Make sure you have free space there, you\(aqll need about 1GB each (+ overhead). .sp If your repository is encrypted and borg needs a passphrase to unlock the key, use: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C BORG_PASSPHRASE=mysecret borg benchmark crud REPO PATH .ft P .fi .UNINDENT .UNINDENT .sp Measurements are done with different input file sizes and counts. The file contents are very artificial (either all zero or all random), thus the measurement results do not necessarily reflect performance with real data. Also, due to the kind of content used, no compression is used in these benchmarks. .INDENT 0.0 .TP .B C\- == borg create (1st archive creation, no compression, do not use files cache) C\-Z\- == all\-zero files. full dedup, this is primarily measuring reader/chunker/hasher. C\-R\- == random files. no dedup, measuring throughput through all processing stages. .TP .B R\- == borg extract (extract archive, dry\-run, do everything, but do not write files to disk) R\-Z\- == all zero files. Measuring heavily duplicated files. R\-R\- == random files. No duplication here, measuring throughput through all processing stages, except writing to disk. .TP .B U\- == borg create (2nd archive creation of unchanged input files, measure files cache speed) The throughput value is kind of virtual here, it does not actually read the file. U\-Z\- == needs to check the 2 all\-zero chunks\(aq existence in the repo. U\-R\- == needs to check existence of a lot of different chunks in the repo. .TP .B D\- == borg delete archive (delete last remaining archive, measure deletion + compaction) D\-Z\- == few chunks to delete / few segments to compact/remove. D\-R\- == many chunks to delete / many segments to compact/remove. .UNINDENT .sp Please note that there might be quite some variance in these measurements. Try multiple measurements and having a otherwise idle machine (and network, if you use it). .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B PATH path were to create benchmark input data .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-benchmark.10000644000076500000240000000214314716225054017170 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-BENCHMARK" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-benchmark \- benchmark command .SH SYNOPSIS .nf borg [common options] benchmark crud ... borg [common options] benchmark cpu ... .fi .sp .SH DESCRIPTION .sp These commands do various benchmarks. .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-benchmark\-crud(1)\fP, \fIborg\-benchmark\-cpu(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-break-lock.10000644000076500000240000000237614716225054017260 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-BREAK-LOCK" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-break-lock \- Break the repository lock (e.g. in case it was left by a dead borg. .SH SYNOPSIS .sp borg [common options] break\-lock [options] .SH DESCRIPTION .sp This command breaks the repository and cache locks. Please use carefully and only while no borg process (on any machine) is trying to access the Cache or the Repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-change-passphrase.10000644000076500000240000000303314716225054020631 0ustar00twstaff.\" Man page generated from reStructuredText. . .TH BORG-CHANGE-PASSPHRASE 1 "2017-11-25" "" "borg backup tool" .SH NAME borg-change-passphrase \- Change repository key file passphrase . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp borg [common options] change\-passphrase [options] [REPOSITORY] .SH DESCRIPTION .sp The key files used for repository encryption are optionally passphrase protected. This command can be used to change this passphrase. .sp Please note that this command only changes the passphrase, but not any secret protected by it (like e.g. encryption/MAC keys or chunker seed). Thus, changing the passphrase after passphrase and borg key got compromised does not protect future (nor past) backups to the same repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .sp REPOSITORY .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-check.10000644000076500000240000002341214716225054016315 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-CHECK" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-check \- Check repository consistency .SH SYNOPSIS .sp borg [common options] check [options] .SH DESCRIPTION .sp The check command verifies the consistency of a repository and its archives. It consists of two major steps: .INDENT 0.0 .IP 1. 3 Checking the consistency of the repository itself. This includes checking the file magic headers, and both the metadata and data of all objects in the repository. The read data is checked by size and hash. Bit rot and other types of accidental damage can be detected this way. Running the repository check can be split into multiple partial checks using \fB\-\-max\-duration\fP\&. When checking a \fI\%ssh://\fP remote repository, please note that the checks run on the server and do not cause significant network traffic. .IP 2. 3 Checking consistency and correctness of the archive metadata and optionally archive data (requires \fB\-\-verify\-data\fP). This includes ensuring that the repository manifest exists, the archive metadata chunk is present, and that all chunks referencing files (items) in the archive exist. This requires reading archive and file metadata, but not data. To scan for archives whose entries were lost from the archive directory, pass \fB\-\-find\-lost\-archives\fP\&. It requires reading all data and is hence very time consuming. To additionally cryptographically verify the file (content) data integrity, pass \fB\-\-verify\-data\fP, which is even more time consuming. .sp When checking archives of a remote repository, archive checks run on the client machine because they require decrypting data and therefore the encryption key. .UNINDENT .sp Both steps can also be run independently. Pass \fB\-\-repository\-only\fP to run the repository checks only, or pass \fB\-\-archives\-only\fP to run the archive checks only. .sp The \fB\-\-max\-duration\fP option can be used to split a long\-running repository check into multiple partial checks. After the given number of seconds the check is interrupted. The next partial check will continue where the previous one stopped, until the full repository has been checked. Assuming a complete check would take 7 hours, then running a daily check with \fB\-\-max\-duration=3600\fP (1 hour) would result in one full repository check per week. Doing a full repository check aborts any previous partial check; the next partial check will restart from the beginning. With partial repository checks you can run neither archive checks, nor enable repair mode. Consequently, if you want to use \fB\-\-max\-duration\fP you must also pass \fB\-\-repository\-only\fP, and must not pass \fB\-\-archives\-only\fP, nor \fB\-\-repair\fP\&. .sp \fBWarning:\fP Please note that partial repository checks (i.e. running it with \fB\-\-max\-duration\fP) can only perform non\-cryptographic checksum checks on the repository files. Enabling partial repository checks excepts archive checks for the same reason. Therefore partial checks may be useful with very large repositories only where a full check would take too long. .sp The \fB\-\-verify\-data\fP option will perform a full integrity verification (as opposed to checking just the xxh64) of data, which means reading the data from the repository, decrypting and decompressing it. It is a complete cryptographic verification and hence very time consuming, but will detect any accidental and malicious corruption. Tamper\-resistance is only guaranteed for encrypted repositories against attackers without access to the keys. You can not use \fB\-\-verify\-data\fP with \fB\-\-repository\-only\fP\&. .sp The \fB\-\-find\-lost\-archives\fP option will also scan the whole repository, but tells Borg to search for lost archive metadata. If Borg encounters any archive metadata that doesn\(aqt match with an archive directory entry (including soft\-deleted archives), it means that an entry was lost. Unless \fBborg compact\fP is called, these archives can be fully restored with \fB\-\-repair\fP\&. Please note that \fB\-\-find\-lost\-archives\fP must read a lot of data from the repository and is thus very time consuming. You can not use \fB\-\-find\-lost\-archives\fP with \fB\-\-repository\-only\fP\&. .SS About repair mode .sp The check command is a readonly task by default. If any corruption is found, Borg will report the issue and proceed with checking. To actually repair the issues found, pass \fB\-\-repair\fP\&. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fB\-\-repair\fP is a \fBPOTENTIALLY DANGEROUS FEATURE\fP and might lead to data loss! This does not just include data that was previously lost anyway, but might include more data for kinds of corruption it is not capable of dealing with. \fBBE VERY CAREFUL!\fP .UNINDENT .UNINDENT .sp Pursuant to the previous warning it is also highly recommended to test the reliability of the hardware running Borg with stress testing software. This especially includes storage and memory testers. Unreliable hardware might lead to additional data loss. .sp It is highly recommended to create a backup of your repository before running in repair mode (i.e. running it with \fB\-\-repair\fP). .sp Repair mode will attempt to fix any corruptions found. Fixing corruptions does not mean recovering lost data: Borg can not magically restore data lost due to e.g. a hardware failure. Repairing a repository means sacrificing some data for the sake of the repository as a whole and the remaining data. Hence it is, by definition, a potentially lossy task. .sp In practice, repair mode hooks into both the repository and archive checks: .INDENT 0.0 .IP 1. 3 When checking the repository\(aqs consistency, repair mode removes corrupted objects from the repository after it did a 2nd try to read them correctly. .IP 2. 3 When checking the consistency and correctness of archives, repair mode might remove whole archives from the manifest if their archive metadata chunk is corrupt or lost. On a chunk level (i.e. the contents of files), repair mode will replace corrupt or lost chunks with a same\-size replacement chunk of zeroes. If a previously zeroed chunk reappears, repair mode will restore this lost chunk using the new chunk. .UNINDENT .sp Most steps taken by repair mode have a one\-time effect on the repository, like removing a lost archive from the repository. However, replacing a corrupt or lost chunk with an all\-zero replacement will have an ongoing effect on the repository: When attempting to extract a file referencing an all\-zero chunk, the \fBextract\fP command will distinctly warn about it. The FUSE filesystem created by the \fBmount\fP command will reject reading such a \(dqzero\-patched\(dq file unless a special mount option is given. .sp As mentioned earlier, Borg might be able to \(dqheal\(dq a \(dqzero\-patched\(dq file in repair mode, if all its previously lost chunks reappear (e.g. via a later backup). This is achieved by Borg not only keeping track of the all\-zero replacement chunks, but also by keeping metadata about the lost chunks. In repair mode Borg will check whether a previously lost chunk reappeared and will replace the all\-zero replacement chunk by the reappeared chunk. If all lost chunks of a \(dqzero\-patched\(dq file reappear, this effectively \(dqheals\(dq the file. Consequently, if lost chunks were repaired earlier, it is advised to run \fB\-\-repair\fP a second time after creating some new backups. .sp If \fB\-\-repair \-\-find\-lost\-archives\fP is given, previously lost entries will be recreated in the archive directory. This is only possible before \fBborg compact\fP would remove the archives\(aq data completely. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS optional arguments .INDENT 0.0 .TP .B \-\-repository\-only only perform repository checks .TP .B \-\-archives\-only only perform archives checks .TP .B \-\-verify\-data perform cryptographic archive data integrity verification (conflicts with \fB\-\-repository\-only\fP) .TP .B \-\-repair attempt to repair any inconsistencies found .TP .B \-\-find\-lost\-archives attempt to find lost archives .TP .BI \-\-max\-duration \ SECONDS do only a partial repo check for max. SECONDS seconds (Default: unlimited) .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-a \ PATTERN\fR,\fB \ \-\-match\-archives \ PATTERN only consider archives matching all patterns. see \(dqborg help match\-archives\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .TP .BI \-\-oldest \ TIMESPAN consider archives between the oldest archive\(aqs timestamp and (oldest + TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newest \ TIMESPAN consider archives between the newest archive\(aqs timestamp and (newest \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-older \ TIMESPAN consider archives older than (now \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newer \ TIMESPAN consider archives newer than (now \- TIMESPAN), e.g. 7d or 12m. .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-common.10000644000076500000240000000520114716225054016524 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-COMMON" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-common \- Common options of Borg commands .SH SYNOPSIS .INDENT 0.0 .TP .B \-h\fP,\fB \-\-help show this help message and exit .TP .B \-\-critical work on log level CRITICAL .TP .B \-\-error work on log level ERROR .TP .B \-\-warning work on log level WARNING (default) .TP .B \-\-info\fP,\fB \-v\fP,\fB \-\-verbose work on log level INFO .TP .B \-\-debug enable debug output, work on log level DEBUG .TP .BI \-\-debug\-topic \ TOPIC enable TOPIC debugging (can be specified multiple times). The logger path is borg.debug. if TOPIC is not fully qualified. .TP .B \-p\fP,\fB \-\-progress show progress information .TP .B \-\-iec format using IEC units (1KiB = 1024B) .TP .B \-\-log\-json Output one JSON object per log line instead of formatted text. .TP .BI \-\-lock\-wait \ SECONDS wait at most SECONDS for acquiring a repository/cache lock (default: 10). .TP .B \-\-show\-version show/log the borg version .TP .B \-\-show\-rc show/log the return code (rc) .TP .BI \-\-umask \ M set umask to M (local only, default: 0077) .TP .BI \-\-remote\-path \ PATH use PATH as borg executable on the remote (default: \(dqborg\(dq) .TP .BI \-\-upload\-ratelimit \ RATE set network upload rate limit in kiByte/s (default: 0=unlimited) .TP .BI \-\-upload\-buffer \ UPLOAD_BUFFER set network upload buffer size in MiB. (default: 0=no buffer) .TP .BI \-\-debug\-profile \ FILE Write execution profile in Borg format into FILE. For local use a Python\-compatible file can be generated by suffixing FILE with \(dq.pyprof\(dq. .TP .BI \-\-rsh \ RSH Use this command to connect to the \(aqborg serve\(aq process (default: \(aqssh\(aq) .TP .BI \-\-socket \ PATH Use UNIX DOMAIN (IPC) socket at PATH for client/server communication with socket: protocol. .TP .BI \-r \ REPO\fR,\fB \ \-\-repo \ REPO repository to use .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-compact.10000644000076500000240000000507514716225054016673 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-COMPACT" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-compact \- Collect garbage in repository .SH SYNOPSIS .sp borg [common options] compact [options] .SH DESCRIPTION .sp Free repository space by deleting unused chunks. .sp borg compact analyzes all existing archives to find out which repository objects are actually used (referenced). It then deletes all unused objects from the repository to free space. .sp Unused objects may result from: .INDENT 0.0 .IP \(bu 2 borg delete or prune usage .IP \(bu 2 interrupted backups (maybe retry the backup first before running compact) .IP \(bu 2 backup of source files that had an I/O error in the middle of their contents and that were skipped due to this .IP \(bu 2 corruption of the repository (e.g. the archives directory having lost entries, see notes below) .UNINDENT .sp You usually don\(aqt want to run \fBborg compact\fP after every write operation, but either regularly (e.g. once a month, possibly together with \fBborg check\fP) or when disk space needs to be freed. .sp \fBImportant:\fP .sp After compacting it is no longer possible to use \fBborg undelete\fP to recover previously soft\-deleted archives. .sp \fBborg compact\fP might also delete data from archives that were \(dqlost\(dq due to archives directory corruption. Such archives could potentially be restored with \fBborg check \-\-find\-lost\-archives [\-\-repair]\fP, which is slow. You therefore might not want to do that unless there are signs of lost archives (e.g. when seeing fatal errors when creating backups or when archives are missing in \fBborg repo\-list\fP). .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # compact segments and free repo disk space $ borg compact .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-compression.10000644000076500000240000001131414716225054017577 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-COMPRESSION" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-compression \- Details regarding compression .SH DESCRIPTION .sp It is no problem to mix different compression methods in one repo, deduplication is done on the source data chunks (not on the compressed or encrypted data). .sp If some specific chunk was once compressed and stored into the repo, creating another backup that also uses this chunk will not change the stored chunk. So if you use different compression specs for the backups, whichever stores a chunk first determines its compression. See also borg recreate. .sp Compression is lz4 by default. If you want something else, you have to specify what you want. .sp Valid compression specifiers are: .INDENT 0.0 .TP .B none Do not compress. .TP .B lz4 Use lz4 compression. Very high speed, very low compression. (default) .TP .B zstd[,L] Use zstd (\(dqzstandard\(dq) compression, a modern wide\-range algorithm. If you do not explicitly give the compression level L (ranging from 1 to 22), it will use level 3. .TP .B zlib[,L] Use zlib (\(dqgz\(dq) compression. Medium speed, medium compression. If you do not explicitly give the compression level L (ranging from 0 to 9), it will use level 6. Giving level 0 (means \(dqno compression\(dq, but still has zlib protocol overhead) is usually pointless, you better use \(dqnone\(dq compression. .TP .B lzma[,L] Use lzma (\(dqxz\(dq) compression. Low speed, high compression. If you do not explicitly give the compression level L (ranging from 0 to 9), it will use level 6. Giving levels above 6 is pointless and counterproductive because it does not compress better due to the buffer size used by borg \- but it wastes lots of CPU cycles and RAM. .TP .B auto,C[,L] Use a built\-in heuristic to decide per chunk whether to compress or not. The heuristic tries with lz4 whether the data is compressible. For incompressible data, it will not use compression (uses \(dqnone\(dq). For compressible data, it uses the given C[,L] compression \- with C[,L] being any valid compression specifier. This can be helpful for media files which often cannot be compressed much more. .TP .B obfuscate,SPEC,C[,L] Use compressed\-size obfuscation to make fingerprinting attacks based on the observable stored chunk size more difficult. Note: .INDENT 7.0 .IP \(bu 2 You must combine this with encryption, or it won\(aqt make any sense. .IP \(bu 2 Your repo size will be bigger, of course. .IP \(bu 2 A chunk is limited by the constant \fBMAX_DATA_SIZE\fP (cur. ~20MiB). .UNINDENT .sp The SPEC value determines how the size obfuscation works: .sp \fIRelative random reciprocal size variation\fP (multiplicative) .sp Size will increase by a factor, relative to the compressed data size. Smaller factors are used often, larger factors rarely. .sp Available factors: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 1: 0.01 .. 100 2: 0.1 .. 1,000 3: 1 .. 10,000 4: 10 .. 100,000 5: 100 .. 1,000,000 6: 1,000 .. 10,000,000 .ft P .fi .UNINDENT .UNINDENT .sp Example probabilities for SPEC \fB1\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 90 % 0.01 .. 0.1 9 % 0.1 .. 1 0.9 % 1 .. 10 0.09% 10 .. 100 .ft P .fi .UNINDENT .UNINDENT .sp \fIRandomly sized padding up to the given size\fP (additive) .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C 110: 1kiB (2 ^ (SPEC \- 100)) \&... 120: 1MiB \&... 123: 8MiB (max.) .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg create \-\-compression lz4 REPO::ARCHIVE data borg create \-\-compression zstd REPO::ARCHIVE data borg create \-\-compression zstd,10 REPO::ARCHIVE data borg create \-\-compression zlib REPO::ARCHIVE data borg create \-\-compression zlib,1 REPO::ARCHIVE data borg create \-\-compression auto,lzma,6 REPO::ARCHIVE data borg create \-\-compression auto,lzma ... borg create \-\-compression obfuscate,110,none ... borg create \-\-compression obfuscate,3,auto,zstd,10 ... borg create \-\-compression obfuscate,2,zstd,6 ... .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-config.10000644000076500000240000000517314716225054016511 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-CONFIG" 1 "2024-07-19" "" "borg backup tool" .SH NAME borg-config \- get, set, and delete values in a repository or cache config file .SH SYNOPSIS .sp borg [common options] config [options] [NAME] [VALUE] .SH DESCRIPTION .sp This command gets and sets options in a local repository or cache config file. For security reasons, this command only works on local repositories. .sp To delete a config value entirely, use \fB\-\-delete\fP\&. To list the values of the configuration file or the default values, use \fB\-\-list\fP\&. To get an existing key, pass only the key name. To set a key, pass both the key name and the new value. Keys can be specified in the format \(dqsection.name\(dq or simply \(dqname\(dq; the section will default to \(dqrepository\(dq and \(dqcache\(dq for the repo and cache configs, respectively. .sp By default, borg config manipulates the repository config file. Using \fB\-\-cache\fP edits the repository cache\(aqs config file instead. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME name of config key .TP .B VALUE new value for key .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-c\fP,\fB \-\-cache get and set values from the repo cache .TP .B \-d\fP,\fB \-\-delete delete the key from the config file .TP .B \-l\fP,\fB \-\-list list the configuration of the repo .UNINDENT .SH EXAMPLES .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The repository & cache config files are some of the only directly manipulable parts of a repository that aren\(aqt versioned or backed up, so be careful when making changes! .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # find cache directory $ cd ~/.cache/borg/$(borg config id) # reserve some space $ borg config additional_free_space 2G # make a repo append\-only $ borg config append_only 1 .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-create.10000644000076500000240000004535414716225054016514 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-CREATE" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-create \- Create new archive .SH SYNOPSIS .sp borg [common options] create [options] NAME [PATH...] .SH DESCRIPTION .sp This command creates a backup archive containing all files found while recursively traversing all paths specified. Paths are added to the archive as they are given, that means if relative paths are desired, the command has to be run from the correct directory. .sp The slashdot hack in paths (recursion roots) is triggered by using \fB/./\fP: \fB/this/gets/stripped/./this/gets/archived\fP means to process that fs object, but strip the prefix on the left side of \fB\&./\fP from the archived items (in this case, \fBthis/gets/archived\fP will be the path in the archived item). .sp When giving \(aq\-\(aq as path, borg will read data from standard input and create a file \(aqstdin\(aq in the created archive from that data. In some cases it\(aqs more appropriate to use \-\-content\-from\-command, however. See section \fIReading from stdin\fP below for details. .sp The archive will consume almost no disk space for files or parts of files that have already been stored in other archives. .sp The archive name does NOT need to be unique, you can and should use the same name for a series of archives. The unique archive identifier is its ID (hash) and you can abbreviate the ID as long as it is unique. .sp In the archive name, you may use the following placeholders: {now}, {utcnow}, {fqdn}, {hostname}, {user} and some others. .sp Backup speed is increased by not reprocessing files that are already part of existing archives and weren\(aqt modified. The detection of unmodified files is done by comparing multiple file metadata values with previous values kept in the files cache. .sp This comparison can operate in different modes as given by \fB\-\-files\-cache\fP: .INDENT 0.0 .IP \(bu 2 ctime,size,inode (default) .IP \(bu 2 mtime,size,inode (default behaviour of borg versions older than 1.1.0rc4) .IP \(bu 2 ctime,size (ignore the inode number) .IP \(bu 2 mtime,size (ignore the inode number) .IP \(bu 2 rechunk,ctime (all files are considered modified \- rechunk, cache ctime) .IP \(bu 2 rechunk,mtime (all files are considered modified \- rechunk, cache mtime) .IP \(bu 2 disabled (disable the files cache, all files considered modified \- rechunk) .UNINDENT .sp inode number: better safety, but often unstable on network filesystems .sp Normally, detecting file modifications will take inode information into consideration to improve the reliability of file change detection. This is problematic for files located on sshfs and similar network file systems which do not provide stable inode numbers, such files will always be considered modified. You can use modes without \fIinode\fP in this case to improve performance, but reliability of change detection might be reduced. .sp ctime vs. mtime: safety vs. speed .INDENT 0.0 .IP \(bu 2 ctime is a rather safe way to detect changes to a file (metadata and contents) as it can not be set from userspace. But, a metadata\-only change will already update the ctime, so there might be some unnecessary chunking/hashing even without content changes. Some filesystems do not support ctime (change time). E.g. doing a chown or chmod to a file will change its ctime. .IP \(bu 2 mtime usually works and only updates if file contents were changed. But mtime can be arbitrarily set from userspace, e.g. to set mtime back to the same value it had before a content change happened. This can be used maliciously as well as well\-meant, but in both cases mtime based cache modes can be problematic. .UNINDENT .sp The mount points of filesystems or filesystem snapshots should be the same for every creation of a new archive to ensure fast operation. This is because the file cache that is used to determine changed files quickly uses absolute filenames. If this is not possible, consider creating a bind mount to a stable location. .sp The \fB\-\-progress\fP option shows (from left to right) Original and (uncompressed) deduplicated size (O and U respectively), then the Number of files (N) processed so far, followed by the currently processed path. .sp When using \fB\-\-stats\fP, you will get some statistics about how much data was added \- the \(dqThis Archive\(dq deduplicated size there is most interesting as that is how much your repository will grow. Please note that the \(dqAll archives\(dq stats refer to the state after creation. Also, the \fB\-\-stats\fP and \fB\-\-dry\-run\fP options are mutually exclusive because the data is not actually compressed and deduplicated during a dry run. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .sp For more help on placeholders, see the \fIborg_placeholders\fP command output. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME specify the archive name .TP .B PATH paths to archive .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-n\fP,\fB \-\-dry\-run do not create a backup archive .TP .B \-s\fP,\fB \-\-stats print statistics for the created archive .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .BI \-\-filter \ STATUSCHARS only display items with the given status characters (see description) .TP .B \-\-json output stats as JSON. Implies \fB\-\-stats\fP\&. .TP .BI \-\-stdin\-name \ NAME use NAME in archive for stdin data (default: \(aqstdin\(aq) .TP .BI \-\-stdin\-user \ USER set user USER in archive for stdin data (default: do not store user/uid) .TP .BI \-\-stdin\-group \ GROUP set group GROUP in archive for stdin data (default: do not store group/gid) .TP .BI \-\-stdin\-mode \ M set mode to M in archive for stdin data (default: 0660) .TP .B \-\-content\-from\-command interpret PATH as command and store its stdout. See also section Reading from stdin below. .TP .B \-\-paths\-from\-stdin read DELIM\-separated list of paths to back up from stdin. All control is external: it will back up all files given \- no more, no less. .TP .B \-\-paths\-from\-command interpret PATH as command and treat its output as \fB\-\-paths\-from\-stdin\fP .TP .BI \-\-paths\-delimiter \ DELIM set path delimiter for \fB\-\-paths\-from\-stdin\fP and \fB\-\-paths\-from\-command\fP (default: \fB\en\fP) .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .B \-\-exclude\-caches exclude directories that contain a CACHEDIR.TAG file (\fI\%http://www.bford.info/cachedir/spec.html\fP) .TP .BI \-\-exclude\-if\-present \ NAME exclude directories that are tagged by containing a filesystem object with the given NAME .TP .B \-\-keep\-exclude\-tags if tag objects are specified with \fB\-\-exclude\-if\-present\fP, don\(aqt omit the tag objects themselves from the backup archive .TP .B \-\-exclude\-nodump exclude files flagged NODUMP .UNINDENT .SS Filesystem options .INDENT 0.0 .TP .B \-x\fP,\fB \-\-one\-file\-system stay in the same file system and do not store mount points of other file systems \- this might behave different from your expectations, see the description below. .TP .B \-\-numeric\-ids only store numeric user and group identifiers .TP .B \-\-atime do store atime into archive .TP .B \-\-noctime do not store ctime into archive .TP .B \-\-nobirthtime do not store birthtime (creation date) into archive .TP .B \-\-noflags do not read and store flags (e.g. NODUMP, IMMUTABLE) into archive .TP .B \-\-noacls do not read and store ACLs into archive .TP .B \-\-noxattrs do not read and store xattrs into archive .TP .B \-\-sparse detect sparse holes in input (supported only by fixed chunker) .TP .BI \-\-files\-cache \ MODE operate files cache in MODE. default: ctime,size,inode .TP .B \-\-read\-special open and read block and char device files as well as FIFOs as if they were regular files. Also follows symlinks pointing to these kinds of files. .UNINDENT .SS Archive options .INDENT 0.0 .TP .BI \-\-comment \ COMMENT add a comment text to the archive .TP .BI \-\-timestamp \ TIMESTAMP manually specify the archive creation date/time (yyyy\-mm\-ddThh:mm:ss[(+|\-)HH:MM] format, (+|\-)HH:MM is the UTC offset, default: local time zone). Alternatively, give a reference file/directory. .TP .BI \-\-chunker\-params \ PARAMS specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 .TP .BI \-C \ COMPRESSION\fR,\fB \ \-\-compression \ COMPRESSION select compression algorithm, see the output of the \(dqborg help compression\(dq command for details. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Backup ~/Documents into an archive named \(dqmy\-documents\(dq $ borg create my\-documents ~/Documents # same, but list all files as we process them $ borg create \-\-list my\-documents ~/Documents # Backup /mnt/disk/docs, but strip path prefix using the slashdot hack $ borg create /path/to/repo::docs /mnt/disk/./docs # Backup ~/Documents and ~/src but exclude pyc files $ borg create my\-files \e ~/Documents \e ~/src \e \-\-exclude \(aq*.pyc\(aq # Backup home directories excluding image thumbnails (i.e. only # /home//.thumbnails is excluded, not /home/*/*/.thumbnails etc.) $ borg create my\-files /home \-\-exclude \(aqsh:home/*/.thumbnails\(aq # Backup the root filesystem into an archive named \(dqroot\-archive\(dq # use zlib compression (good, but slow) \- default is lz4 (fast, low compression ratio) $ borg create \-C zlib,6 \-\-one\-file\-system root\-archive / # Backup into an archive name like FQDN\-root $ borg create \(aq{fqdn}\-root\(aq / # Backup a remote host locally (\(dqpull\(dq style) using sshfs $ mkdir sshfs\-mount $ sshfs root@example.com:/ sshfs\-mount $ cd sshfs\-mount $ borg create example.com\-root . $ cd .. $ fusermount \-u sshfs\-mount # Make a big effort in fine granular deduplication (big chunk management # overhead, needs a lot of RAM and disk space, see formula in internals docs): $ borg create \-\-chunker\-params buzhash,10,23,16,4095 small /smallstuff # Backup a raw device (must not be active/in use/mounted at that time) $ borg create \-\-read\-special \-\-chunker\-params fixed,4194304 my\-sdx /dev/sdX # Backup a sparse disk image (must not be active/in use/mounted at that time) $ borg create \-\-sparse \-\-chunker\-params fixed,4194304 my\-disk my\-disk.raw # No compression (none) $ borg create \-\-compression none arch ~ # Super fast, low compression (lz4, default) $ borg create arch ~ # Less fast, higher compression (zlib, N = 0..9) $ borg create \-\-compression zlib,N arch ~ # Even slower, even higher compression (lzma, N = 0..9) $ borg create \-\-compression lzma,N arch ~ # Only compress compressible data with lzma,N (N = 0..9) $ borg create \-\-compression auto,lzma,N arch ~ # Use short hostname and user name as archive name $ borg create \(aq{hostname}\-{user}\(aq ~ # Backing up relative paths by moving into the correct directory first $ cd /home/user/Documents # The root directory of the archive will be \(dqprojectA\(dq $ borg create \(aqdaily\-projectA\(aq projectA # Use external command to determine files to archive # Use \-\-paths\-from\-stdin with find to back up only files less than 1MB in size $ find ~ \-size \-1000k | borg create \-\-paths\-from\-stdin small\-files\-only # Use \-\-paths\-from\-command with find to back up files from only a given user $ borg create \-\-paths\-from\-command joes\-files \-\- find /srv/samba/shared \-user joe # Use \-\-paths\-from\-stdin with \-\-paths\-delimiter (for example, for filenames with newlines in them) $ find ~ \-size \-1000k \-print0 | borg create \e \-\-paths\-from\-stdin \e \-\-paths\-delimiter \(dq\e0\(dq \e smallfiles\-handle\-newline .ft P .fi .UNINDENT .UNINDENT .SH NOTES .sp The \fB\-\-exclude\fP patterns are not like tar. In tar \fB\-\-exclude\fP .bundler/gems will exclude foo/.bundler/gems. In borg it will not, you need to use \fB\-\-exclude\fP \(aq*/.bundler/gems\(aq to get the same effect. .sp In addition to using \fB\-\-exclude\fP patterns, it is possible to use \fB\-\-exclude\-if\-present\fP to specify the name of a filesystem object (e.g. a file or folder name) which, when contained within another folder, will prevent the containing folder from being backed up. By default, the containing folder and all of its contents will be omitted from the backup. If, however, you wish to only include the objects specified by \fB\-\-exclude\-if\-present\fP in your backup, and not include any other contents of the containing folder, this can be enabled through using the \fB\-\-keep\-exclude\-tags\fP option. .sp The \fB\-x\fP or \fB\-\-one\-file\-system\fP option excludes directories, that are mountpoints (and everything in them). It detects mountpoints by comparing the device number from the output of \fBstat()\fP of the directory and its parent directory. Specifically, it excludes directories for which \fBstat()\fP reports a device number different from the device number of their parent. In general: be aware that there are directories with device number different from their parent, which the kernel does not consider a mountpoint and also the other way around. Linux examples for this are bind mounts (possibly same device number, but always a mountpoint) and ALL subvolumes of a btrfs (different device number from parent but not necessarily a mountpoint). macOS examples are the apfs mounts of a typical macOS installation. Therefore, when using \fB\-\-one\-file\-system\fP, you should double\-check that the backup works as intended. .SS Item flags .sp \fB\-\-list\fP outputs a list of all files, directories and other file system items it considered (no matter whether they had content changes or not). For each item, it prefixes a single\-letter flag that indicates type and/or status of the item. .sp If you are interested only in a subset of that output, you can give e.g. \fB\-\-filter=AME\fP and it will only show regular files with A, M or E status (see below). .sp A uppercase character represents the status of a regular file relative to the \(dqfiles\(dq cache (not relative to the repo \-\- this is an issue if the files cache is not used). Metadata is stored in any case and for \(aqA\(aq and \(aqM\(aq also new data chunks are stored. For \(aqU\(aq all data chunks refer to already existing chunks. .INDENT 0.0 .IP \(bu 2 \(aqA\(aq = regular file, added (see also \fIa_status_oddity\fP in the FAQ) .IP \(bu 2 \(aqM\(aq = regular file, modified .IP \(bu 2 \(aqU\(aq = regular file, unchanged .IP \(bu 2 \(aqC\(aq = regular file, it changed while we backed it up .IP \(bu 2 \(aqE\(aq = regular file, an error happened while accessing/reading \fIthis\fP file .UNINDENT .sp A lowercase character means a file type other than a regular file, borg usually just stores their metadata: .INDENT 0.0 .IP \(bu 2 \(aqd\(aq = directory .IP \(bu 2 \(aqb\(aq = block device .IP \(bu 2 \(aqc\(aq = char device .IP \(bu 2 \(aqh\(aq = regular file, hardlink (to already seen inodes) .IP \(bu 2 \(aqs\(aq = symlink .IP \(bu 2 \(aqf\(aq = fifo .UNINDENT .sp Other flags used include: .INDENT 0.0 .IP \(bu 2 \(aq+\(aq = included, item would be backed up (if not in dry\-run mode) .IP \(bu 2 \(aq\-\(aq = excluded, item would not be / was not backed up .IP \(bu 2 \(aqi\(aq = backup data was read from standard input (stdin) .IP \(bu 2 \(aq?\(aq = missing status code (if you see this, please file a bug report!) .UNINDENT .SS Reading backup data from stdin .sp There are two methods to read from stdin. Either specify \fB\-\fP as path and pipe directly to borg: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C backup\-vm \-\-id myvm \-\-stdout | borg create REPO::ARCHIVE \- .ft P .fi .UNINDENT .UNINDENT .sp Or use \fB\-\-content\-from\-command\fP to have Borg manage the execution of the command and piping. If you do so, the first PATH argument is interpreted as command to execute and any further arguments are treated as arguments to the command: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg create \-\-content\-from\-command REPO::ARCHIVE \-\- backup\-vm \-\-id myvm \-\-stdout .ft P .fi .UNINDENT .UNINDENT .sp \fB\-\-\fP is used to ensure \fB\-\-id\fP and \fB\-\-stdout\fP are \fBnot\fP considered arguments to \fBborg\fP but rather \fBbackup\-vm\fP\&. .sp The difference between the two approaches is that piping to borg creates an archive even if the command piping to borg exits with a failure. In this case, \fBone can end up with truncated output being backed up\fP\&. Using \fB\-\-content\-from\-command\fP, in contrast, borg is guaranteed to fail without creating an archive should the command fail. The command is considered failed when it returned a non\-zero exit code. .sp Reading from stdin yields just a stream of data without file metadata associated with it, and the files cache is not needed at all. So it is safe to disable it via \fB\-\-files\-cache disabled\fP and speed up backup creation a bit. .sp By default, the content read from stdin is stored in a file called \(aqstdin\(aq. Use \fB\-\-stdin\-name\fP to change the name. .SS Feeding all file paths from externally .sp Usually, you give a starting path (recursion root) to borg and then borg automatically recurses, finds and backs up all fs objects contained in there (optionally considering include/exclude rules). .sp If you need more control and you want to give every single fs object path to borg (maybe implementing your own recursion or your own rules), you can use \fB\-\-paths\-from\-stdin\fP or \fB\-\-paths\-from\-command\fP (with the latter, borg will fail to create an archive should the command fail). .sp Borg supports paths with the slashdot hack to strip path prefixes here also. So, be careful not to unintentionally trigger that. .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-delete(1)\fP, \fIborg\-prune(1)\fP, \fIborg\-check(1)\fP, \fIborg\-patterns(1)\fP, \fIborg\-placeholders(1)\fP, \fIborg\-compression(1)\fP, \fIborg\-repo\-create(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-delete.10000644000076500000240000000701014716225054016476 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-DELETE" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-delete \- Delete archives .SH SYNOPSIS .sp borg [common options] delete [options] [NAME] .SH DESCRIPTION .sp This command soft\-deletes archives from the repository. .sp Important: .INDENT 0.0 .IP \(bu 2 The delete command will only mark archives for deletion (\(dqsoft\-deletion\(dq), repository disk space is \fBnot\fP freed until you run \fBborg compact\fP\&. .IP \(bu 2 You can use \fBborg undelete\fP to undelete archives, but only until you run \fBborg compact\fP\&. .UNINDENT .sp When in doubt, use \fB\-\-dry\-run \-\-list\fP to see what would be deleted. .sp You can delete multiple archives by specifying a matching pattern, using the \fB\-\-match\-archives PATTERN\fP option (for more info on these patterns, see \fIborg_patterns\fP). .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME specify the archive name .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-n\fP,\fB \-\-dry\-run do not change repository .TP .B \-\-list output verbose list of archives .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-a \ PATTERN\fR,\fB \ \-\-match\-archives \ PATTERN only consider archives matching all patterns. see \(dqborg help match\-archives\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .TP .BI \-\-oldest \ TIMESPAN consider archives between the oldest archive\(aqs timestamp and (oldest + TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newest \ TIMESPAN consider archives between the newest archive\(aqs timestamp and (newest \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-older \ TIMESPAN consider archives older than (now \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newer \ TIMESPAN consider archives newer than (now \- TIMESPAN), e.g. 7d or 12m. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # delete all backup archives named \(dqkenny\-files\(dq: $ borg delete \-a kenny\-files # actually free disk space: $ borg compact # delete a specific backup archive using its unique archive ID prefix $ borg delete aid:d34db33f # delete all archives whose names begin with the machine\(aqs hostname followed by \(dq\-\(dq $ borg delete \-a \(aqsh:{hostname}\-*\(aq # delete all archives whose names contain \(dq\-2012\-\(dq $ borg delete \-a \(aqsh:*\-2012\-*\(aq # see what would be deleted if delete was run without \-\-dry\-run $ borg delete \-\-list \-\-dry\-run \-a \(aqsh:*\-May\-*\(aq .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-compact(1)\fP, \fIborg\-repo\-delete(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-diff.10000644000076500000240000001147214716225054016153 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-DIFF" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-diff \- Diff contents of two archives .SH SYNOPSIS .sp borg [common options] diff [options] ARCHIVE1 ARCHIVE2 [PATH...] .SH DESCRIPTION .sp This command finds differences (file contents, metadata) between ARCHIVE1 and ARCHIVE2. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B ARCHIVE1 ARCHIVE1 name .TP .B ARCHIVE2 ARCHIVE2 name .TP .B PATH paths of items inside the archives to compare; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-numeric\-ids only consider numeric user and group identifiers .TP .B \-\-same\-chunker\-params Override check of chunker parameters. .TP .B \-\-sort Sort the output lines by file path. .TP .BI \-\-format \ FORMAT specify format for differences between archives (default: \(dq{change} {path}{NL}\(dq) .TP .B \-\-json\-lines Format output as JSON Lines. .TP .B \-\-content\-only Only compare differences in content (exclude metadata differences) .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg diff archive1 archive2 +17 B \-5 B [\-rw\-r\-\-r\-\- \-> \-rwxr\-xr\-x] file1 +135 B \-252 B file2 added 0 B file4 removed 0 B file3 $ borg diff archive1 archive2 {\(dqpath\(dq: \(dqfile1\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqmodified\(dq, \(dqadded\(dq: 17, \(dqremoved\(dq: 5}, {\(dqtype\(dq: \(dqmode\(dq, \(dqold_mode\(dq: \(dq\-rw\-r\-\-r\-\-\(dq, \(dqnew_mode\(dq: \(dq\-rwxr\-xr\-x\(dq}]} {\(dqpath\(dq: \(dqfile2\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqmodified\(dq, \(dqadded\(dq: 135, \(dqremoved\(dq: 252}]} {\(dqpath\(dq: \(dqfile4\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqadded\(dq, \(dqsize\(dq: 0}]} {\(dqpath\(dq: \(dqfile3\(dq, \(dqchanges\(dq: [{\(dqtype\(dq: \(dqremoved\(dq, \(dqsize\(dq: 0}]} .ft P .fi .UNINDENT .UNINDENT .SH NOTES .SS The FORMAT specifier syntax .sp The \fB\-\-format\fP option uses python\(aqs \fI\%format string syntax\fP\&. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg diff \-\-format \(aq{content:30} {path}{NL}\(aq ArchiveFoo ArchiveBar modified: +4.1 kB \-1.0 kB file\-diff \&... # {VAR:NUMBER} \- pad to NUMBER columns right\-aligned. $ borg diff \-\-format \(aq{content:>30} {path}{NL}\(aq ArchiveFoo ArchiveBar modified: +4.1 kB \-1.0 kB file\-diff \&... .ft P .fi .UNINDENT .UNINDENT .sp The following keys are always available: .INDENT 0.0 .IP \(bu 2 NEWLINE: OS dependent line separator .IP \(bu 2 NL: alias of NEWLINE .IP \(bu 2 NUL: NUL character for creating print0 / xargs \-0 like output .IP \(bu 2 SPACE: space character .IP \(bu 2 TAB: tab character .IP \(bu 2 CR: carriage return character .IP \(bu 2 LF: line feed character .UNINDENT .sp Keys available only when showing differences between archives: .INDENT 0.0 .IP \(bu 2 path: archived file path .IP \(bu 2 change: all available changes .IP \(bu 2 content: file content change .IP \(bu 2 mode: file mode change .IP \(bu 2 type: file type change .IP \(bu 2 owner: file owner (user/group) change .IP \(bu 2 group: file group change .IP \(bu 2 user: file user change .IP \(bu 2 link: file link change .IP \(bu 2 directory: file directory change .IP \(bu 2 blkdev: file block device change .IP \(bu 2 chrdev: file character device change .IP \(bu 2 fifo: file fifo change .IP \(bu 2 mtime: file modification time change .IP \(bu 2 ctime: file change time change .IP \(bu 2 isomtime: file modification time change (ISO 8601) .IP \(bu 2 isoctime: file creation time change (ISO 8601) .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-export-tar.10000644000076500000240000000723014716225054017345 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-EXPORT-TAR" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-export-tar \- Export archive contents as a tarball .SH SYNOPSIS .sp borg [common options] export\-tar [options] NAME FILE [PATH...] .SH DESCRIPTION .sp This command creates a tarball from an archive. .sp When giving \(aq\-\(aq as the output FILE, Borg will write a tar stream to standard output. .sp By default (\fB\-\-tar\-filter=auto\fP) Borg will detect whether the FILE should be compressed based on its file extension and pipe the tarball through an appropriate filter before writing it to FILE: .INDENT 0.0 .IP \(bu 2 \&.tar.gz or .tgz: gzip .IP \(bu 2 \&.tar.bz2 or .tbz: bzip2 .IP \(bu 2 \&.tar.xz or .txz: xz .IP \(bu 2 \&.tar.zstd or .tar.zst: zstd .IP \(bu 2 \&.tar.lz4: lz4 .UNINDENT .sp Alternatively, a \fB\-\-tar\-filter\fP program may be explicitly specified. It should read the uncompressed tar stream from stdin and write a compressed/filtered tar stream to stdout. .sp Depending on the \fB\-tar\-format\fP option, these formats are created: .TS center; |l|l|l|. _ T{ \-\-tar\-format T} T{ Specification T} T{ Metadata T} _ T{ BORG T} T{ BORG specific, like PAX T} T{ all as supported by borg T} _ T{ PAX T} T{ POSIX.1\-2001 (pax) format T} T{ GNU + atime/ctime/mtime ns + xattrs T} _ T{ GNU T} T{ GNU tar format T} T{ mtime s, no atime/ctime, no ACLs/xattrs/bsdflags T} _ .TE .sp A \fB\-\-sparse\fP option (as found in borg extract) is not supported. .sp By default the entire archive is extracted but a subset of files and directories can be selected by passing a list of \fBPATHs\fP as arguments. The file selection can further be restricted by using the \fB\-\-exclude\fP option. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .sp \fB\-\-progress\fP can be slower than no progress display, since it makes one additional pass over the archive metadata. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME specify the archive name .TP .B FILE output tar file. \(dq\-\(dq to write to stdout instead. .TP .B PATH paths to extract; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-tar\-filter filter program to pipe data through .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .BI \-\-tar\-format \ FMT select tar format: BORG, PAX or GNU .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .BI \-\-strip\-components \ NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-extract.10000644000076500000240000001014414716225054016710 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-EXTRACT" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-extract \- Extract archive contents .SH SYNOPSIS .sp borg [common options] extract [options] NAME [PATH...] .SH DESCRIPTION .sp This command extracts the contents of an archive. By default the entire archive is extracted but a subset of files and directories can be selected by passing a list of \fBPATHs\fP as arguments. The file selection can further be restricted by using the \fB\-\-exclude\fP option. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .sp By using \fB\-\-dry\-run\fP, you can do all extraction steps except actually writing the output data: reading metadata and data chunks from the repo, checking the hash/hmac, decrypting, decompressing. .sp \fB\-\-progress\fP can be slower than no progress display, since it makes one additional pass over the archive metadata. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Currently, extract always writes into the current working directory (\(dq.\(dq), so make sure you \fBcd\fP to the right place before calling \fBborg extract\fP\&. .sp When parent directories are not extracted (because of using file/directory selection or any other reason), borg can not restore parent directories\(aq metadata, e.g. owner, group, permission, etc. .UNINDENT .UNINDENT .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME specify the archive name .TP .B PATH paths to extract; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .B \-n\fP,\fB \-\-dry\-run do not actually change any files .TP .B \-\-numeric\-ids only obey numeric user and group identifiers .TP .B \-\-noflags do not extract/set flags (e.g. NODUMP, IMMUTABLE) .TP .B \-\-noacls do not extract/set ACLs .TP .B \-\-noxattrs do not extract/set xattrs .TP .B \-\-stdout write all extracted data to stdout .TP .B \-\-sparse create holes in output sparse file from all\-zero chunks .TP .B \-\-continue continue a previously interrupted extraction of same archive .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .BI \-\-strip\-components \ NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Extract entire archive $ borg extract my\-files # Extract entire archive and list files while processing $ borg extract \-\-list my\-files # Verify whether an archive could be successfully extracted, but do not write files to disk $ borg extract \-\-dry\-run my\-files # Extract the \(dqsrc\(dq directory $ borg extract my\-files home/USERNAME/src # Extract the \(dqsrc\(dq directory but exclude object files $ borg extract my\-files home/USERNAME/src \-\-exclude \(aq*.o\(aq # Restore a raw device (must not be active/in use/mounted at that time) $ borg extract \-\-stdout my\-sdx | dd of=/dev/sdx bs=10M .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-mount(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-import-tar.10000644000076500000240000001304514716225054017337 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-IMPORT-TAR" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-import-tar \- Create a backup archive from a tarball .SH SYNOPSIS .sp borg [common options] import\-tar [options] NAME TARFILE .SH DESCRIPTION .sp This command creates a backup archive from a tarball. .sp When giving \(aq\-\(aq as path, Borg will read a tar stream from standard input. .sp By default (\-\-tar\-filter=auto) Borg will detect whether the file is compressed based on its file extension and pipe the file through an appropriate filter: .INDENT 0.0 .IP \(bu 2 \&.tar.gz or .tgz: gzip \-d .IP \(bu 2 \&.tar.bz2 or .tbz: bzip2 \-d .IP \(bu 2 \&.tar.xz or .txz: xz \-d .IP \(bu 2 \&.tar.zstd or .tar.zst: zstd \-d .IP \(bu 2 \&.tar.lz4: lz4 \-d .UNINDENT .sp Alternatively, a \-\-tar\-filter program may be explicitly specified. It should read compressed data from stdin and output an uncompressed tar stream on stdout. .sp Most documentation of borg create applies. Note that this command does not support excluding files. .sp A \fB\-\-sparse\fP option (as found in borg create) is not supported. .sp About tar formats and metadata conservation or loss, please see \fBborg export\-tar\fP\&. .sp import\-tar reads these tar formats: .INDENT 0.0 .IP \(bu 2 BORG: borg specific (PAX\-based) .IP \(bu 2 PAX: POSIX.1\-2001 .IP \(bu 2 GNU: GNU tar .IP \(bu 2 POSIX.1\-1988 (ustar) .IP \(bu 2 UNIX V7 tar .IP \(bu 2 SunOS tar with extended attributes .UNINDENT .sp To import multiple tarballs into a single archive, they can be simply concatenated (e.g. using \(dqcat\(dq) into a single file, and imported with an \fB\-\-ignore\-zeros\fP option to skip through the stop markers between them. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME specify the archive name .TP .B TARFILE input tar file. \(dq\-\(dq to read from stdin instead. .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-tar\-filter filter program to pipe data through .TP .B \-s\fP,\fB \-\-stats print statistics for the created archive .TP .B \-\-list output verbose list of items (files, dirs, ...) .TP .BI \-\-filter \ STATUSCHARS only display items with the given status characters .TP .B \-\-json output stats as JSON (implies \-\-stats) .TP .B \-\-ignore\-zeros ignore zero\-filled blocks in the input tarball .UNINDENT .SS Archive options .INDENT 0.0 .TP .BI \-\-comment \ COMMENT add a comment text to the archive .TP .BI \-\-timestamp \ TIMESTAMP manually specify the archive creation date/time (yyyy\-mm\-ddThh:mm:ss[(+|\-)HH:MM] format, (+|\-)HH:MM is the UTC offset, default: local time zone). Alternatively, give a reference file/directory. .TP .BI \-\-chunker\-params \ PARAMS specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, HASH_MASK_BITS, HASH_WINDOW_SIZE). default: buzhash,19,23,21,4095 .TP .BI \-C \ COMPRESSION\fR,\fB \ \-\-compression \ COMPRESSION select compression algorithm, see the output of the \(dqborg help compression\(dq command for details. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # export as uncompressed tar $ borg export\-tar Monday Monday.tar # import an uncompressed tar $ borg import\-tar Monday Monday.tar # exclude some file types, compress using gzip $ borg export\-tar Monday Monday.tar.gz \-\-exclude \(aq*.so\(aq # use higher compression level with gzip $ borg export\-tar \-\-tar\-filter=\(dqgzip \-9\(dq Monday Monday.tar.gz # copy an archive from repoA to repoB $ borg \-r repoA export\-tar \-\-tar\-format=BORG archive \- | borg \-r repoB import\-tar archive \- # export a tar, but instead of storing it on disk, upload it to remote site using curl $ borg export\-tar Monday \- | curl \-\-data\-binary @\- https://somewhere/to/POST # remote extraction via \(dqtarpipe\(dq $ borg export\-tar Monday \- | ssh somewhere \(dqcd extracted; tar x\(dq .ft P .fi .UNINDENT .UNINDENT .SS Archives transfer script .sp Outputs a script that copies all archives from repo1 to repo2: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C for N I T in \(gaborg list \-\-format=\(aq{archive} {id} {time:%Y\-%m\-%dT%H:%M:%S}{NL}\(aq\(ga do echo \(dqborg \-r repo1 export\-tar \-\-tar\-format=BORG aid:$I \- | borg \-r repo2 import\-tar \-\-timestamp=$T $N \-\(dq done .ft P .fi .UNINDENT .UNINDENT .sp Kept: .INDENT 0.0 .IP \(bu 2 archive name, archive timestamp .IP \(bu 2 archive contents (all items with metadata and data) .UNINDENT .sp Lost: .INDENT 0.0 .IP \(bu 2 some archive metadata (like the original commandline, execution time, etc.) .UNINDENT .sp Please note: .INDENT 0.0 .IP \(bu 2 all data goes over that pipe, again and again for every archive .IP \(bu 2 the pipe is dumb, there is no data or transfer time reduction there due to deduplication .IP \(bu 2 maybe add compression .IP \(bu 2 pipe over ssh for remote transfer .IP \(bu 2 no special sparse file support .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-info.10000644000076500000240000000633314716225054016176 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-INFO" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-info \- Show archive details such as disk space used .SH SYNOPSIS .sp borg [common options] info [options] [NAME] .SH DESCRIPTION .sp This command displays detailed information about the specified archive. .sp Please note that the deduplicated sizes of the individual archives do not add up to the deduplicated size of the repository (\(dqall archives\(dq), because the two are meaning different things: .sp This archive / deduplicated size = amount of data stored ONLY for this archive = unique chunks of this archive. All archives / deduplicated size = amount of data stored in the repo = all chunks in the repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME specify the archive name .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-json format output as JSON .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-a \ PATTERN\fR,\fB \ \-\-match\-archives \ PATTERN only consider archives matching all patterns. see \(dqborg help match\-archives\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .TP .BI \-\-oldest \ TIMESPAN consider archives between the oldest archive\(aqs timestamp and (oldest + TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newest \ TIMESPAN consider archives between the newest archive\(aqs timestamp and (newest \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-older \ TIMESPAN consider archives older than (now \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newer \ TIMESPAN consider archives newer than (now \- TIMESPAN), e.g. 7d or 12m. .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg info aid:f7dea078 Archive name: source\-backup Archive fingerprint: f7dea0788dfc026cc2be1c0f5b94beb4e4084eb3402fc40c38d8719b1bf2d943 Comment: Hostname: mba2020 Username: tw Time (start): Sat, 2022\-06\-25 20:51:40 Time (end): Sat, 2022\-06\-25 20:51:40 Duration: 0.03 seconds Command line: /usr/bin/borg \-r path/to/repo create source\-backup src Utilization of maximum supported archive size: 0% Number of files: 244 Original size: 13.80 MB Deduplicated size: 531 B .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-list(1)\fP, \fIborg\-diff(1)\fP, \fIborg\-repo\-info(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-key-change-algorithm.10000644000076500000240000000533314716225054021241 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-CHANGE-ALGORITHM" 1 "2022-06-26" "" "borg backup tool" .SH NAME borg-key-change-algorithm \- Change repository key algorithm .SH SYNOPSIS .sp borg [common options] key change\-algorithm [options] ALGORITHM .SH DESCRIPTION .sp Change the algorithm we use to encrypt and authenticate the borg key. .sp Important: In a \fIrepokey\fP mode (e.g. repokey\-blake2) all users share the same key. In this mode upgrading to \fIargon2\fP will make it impossible to access the repo for users who use an old version of borg. We recommend upgrading to the latest stable version. .sp Important: In a \fIkeyfile\fP mode (e.g. keyfile\-blake2) each user has their own key (in \fB~/.config/borg/keys\fP). In this mode this command will only change the key used by the current user. If you want to upgrade to \fIargon2\fP to strengthen security, you will have to upgrade each user\(aqs key individually. .sp Your repository is encrypted and authenticated with a key that is randomly generated by \fBborg init\fP\&. The key is encrypted and authenticated with your passphrase. .sp We currently support two choices: .INDENT 0.0 .IP 1. 3 argon2 \- recommended. This algorithm is used by default when initialising a new repository. The key encryption key is derived from your passphrase via argon2\-id. Argon2 is considered more modern and secure than pbkdf2. .IP 2. 3 pbkdf2 \- the legacy algorithm. Use this if you want to access your repo via old versions of borg. The key encryption key is derived from your passphrase via PBKDF2\-HMAC\-SHA256. .UNINDENT .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Upgrade an existing key to argon2 borg key change\-algorithm /path/to/repo argon2 # Downgrade to pbkdf2 \- use this if upgrading borg is not an option borg key change\-algorithm /path/to/repo pbkdf2 .ft P .fi .UNINDENT .UNINDENT .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B ALGORITHM select key algorithm .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-key-change-location.10000644000076500000240000000323214716225054021057 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-CHANGE-LOCATION" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-key-change-location \- Change repository key location .SH SYNOPSIS .sp borg [common options] key change\-location [options] KEY_LOCATION .SH DESCRIPTION .sp Change the location of a borg key. The key can be stored at different locations: .INDENT 0.0 .IP \(bu 2 keyfile: locally, usually in the home directory .IP \(bu 2 repokey: inside the repo (in the repo config) .UNINDENT .sp Please note: .sp This command does NOT change the crypto algorithms, just the key location, thus you must ONLY give the key location (keyfile or repokey). .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B KEY_LOCATION select key location .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-keep keep the key also at the current location (default: remove it) .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-key-change-passphrase.10000644000076500000240000000556714716225054021435 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-CHANGE-PASSPHRASE" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-key-change-passphrase \- Change repository key file passphrase .SH SYNOPSIS .sp borg [common options] key change\-passphrase [options] .SH DESCRIPTION .sp The key files used for repository encryption are optionally passphrase protected. This command can be used to change this passphrase. .sp Please note that this command only changes the passphrase, but not any secret protected by it (like e.g. encryption/MAC keys or chunker seed). Thus, changing the passphrase after passphrase and borg key got compromised does not protect future (nor past) backups to the same repository. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Create a key file protected repository $ borg repo\-create \-\-encryption=keyfile\-aes\-ocb \-v Initializing repository at \(dq/path/to/repo\(dq Enter new passphrase: Enter same passphrase again: Remember your passphrase. Your data will be inaccessible without it. Key in \(dq/root/.config/borg/keys/mnt_backup\(dq created. Keep this key safe. Your data will be inaccessible without it. Synchronizing chunks cache... Archives: 0, w/ cached Idx: 0, w/ outdated Idx: 0, w/o cached Idx: 0. Done. # Change key file passphrase $ borg key change\-passphrase \-v Enter passphrase for key /root/.config/borg/keys/mnt_backup: Enter new passphrase: Enter same passphrase again: Remember your passphrase. Your data will be inaccessible without it. Key updated # Import a previously\-exported key into the specified # key file (creating or overwriting the output key) # (keyfile repositories only) $ BORG_KEY_FILE=/path/to/output\-key borg key import /path/to/exported .ft P .fi .UNINDENT .UNINDENT .sp Fully automated using environment variables: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ BORG_NEW_PASSPHRASE=old borg repo\-create \-\-encryption=repokey\-aes\-ocb # now \(dqold\(dq is the current passphrase. $ BORG_PASSPHRASE=old BORG_NEW_PASSPHRASE=new borg key change\-passphrase # now \(dqnew\(dq is the current passphrase. .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-key-export.10000644000076500000240000000574514716225054017360 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-EXPORT" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-key-export \- Export the repository key for backup .SH SYNOPSIS .sp borg [common options] key export [options] [PATH] .SH DESCRIPTION .sp If repository encryption is used, the repository is inaccessible without the key. This command allows one to back up this essential key. Note that the backup produced does not include the passphrase itself (i.e. the exported key stays encrypted). In order to regain access to a repository, one needs both the exported key and the original passphrase. .sp There are three backup formats. The normal backup format is suitable for digital storage as a file. The \fB\-\-paper\fP backup format is optimized for printing and typing in while importing, with per line checks to reduce problems with manual input. The \fB\-\-qr\-html\fP creates a printable HTML template with a QR code and a copy of the \fB\-\-paper\fP\-formatted key. .sp For repositories using keyfile encryption the key is saved locally on the system that is capable of doing backups. To guard against loss of this key, the key needs to be backed up independently of the main data backup. .sp For repositories using the repokey encryption the key is saved in the repository in the config file. A backup is thus not strictly needed, but guards against the repository becoming inaccessible if the file is damaged for some reason. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B PATH where to store the backup .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-paper Create an export suitable for printing and later type\-in .TP .B \-\-qr\-html Create an html file suitable for printing and later type\-in or qr scan .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C borg key export > encrypted\-key\-backup borg key export \-\-paper > encrypted\-key\-backup.txt borg key export \-\-qr\-html > encrypted\-key\-backup.html # Or pass the output file as an argument instead of redirecting stdout: borg key export encrypted\-key\-backup borg key export \-\-paper encrypted\-key\-backup.txt borg key export \-\-qr\-html encrypted\-key\-backup.html .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-key\-import(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-key-import.10000644000076500000240000000422514716225054017341 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-IMPORT" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-key-import \- Import the repository key from backup .SH SYNOPSIS .sp borg [common options] key import [options] [PATH] .SH DESCRIPTION .sp This command restores a key previously backed up with the export command. .sp If the \fB\-\-paper\fP option is given, the import will be an interactive process in which each line is checked for plausibility before proceeding to the next line. For this format PATH must not be given. .sp For repositories using keyfile encryption, the key file which \fBborg key import\fP writes to depends on several factors. If the \fBBORG_KEY_FILE\fP environment variable is set and non\-empty, \fBborg key import\fP creates or overwrites that file named by \fB$BORG_KEY_FILE\fP\&. Otherwise, \fBborg key import\fP searches in the \fB$BORG_KEYS_DIR\fP directory for a key file associated with the repository. If a key file is found in \fB$BORG_KEYS_DIR\fP, \fBborg key import\fP overwrites it; otherwise, \fBborg key import\fP creates a new key file in \fB$BORG_KEYS_DIR\fP\&. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B PATH path to the backup (\(aq\-\(aq to read from stdin) .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-paper interactively import from a backup done with \fB\-\-paper\fP .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-key\-export(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-key-migrate-to-repokey.10000644000076500000240000000364314716225054021556 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY-MIGRATE-TO-REPOKEY" 1 "2022-02-19" "" "borg backup tool" .SH NAME borg-key-migrate-to-repokey \- Migrate passphrase -> repokey .SH SYNOPSIS .sp borg [common options] key migrate\-to\-repokey [options] [REPOSITORY] .SH DESCRIPTION .sp This command migrates a repository from passphrase mode (removed in Borg 1.0) to repokey mode. .sp You will be first asked for the repository passphrase (to open it in passphrase mode). This is the same passphrase as you used to use for this repo before 1.0. .sp It will then derive the different secrets from this passphrase. .sp Then you will be asked for a new passphrase (twice, for safety). This passphrase will be used to protect the repokey (which contains these same secrets in encrypted form). You may use the same passphrase as you used to use, but you may also use a different one. .sp After migrating to repokey mode, you can change the passphrase at any time. But please note: the secrets will always stay the same and they could always be derived from your (old) passphrase\-mode passphrase. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .sp REPOSITORY .SH SEE ALSO .sp \fIborg\-common(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-key.10000644000076500000240000000232714716225054016032 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-KEY" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-key \- Manage a keyfile or repokey of a repository .SH SYNOPSIS .nf borg [common options] key export ... borg [common options] key import ... borg [common options] key change\-passphrase ... borg [common options] key change\-location ... .fi .sp .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-key\-export(1)\fP, \fIborg\-key\-import(1)\fP, \fIborg\-key\-change\-passphrase(1)\fP, \fIborg\-key\-change\-location(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-list.10000644000076500000240000001475214716225054016222 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-LIST" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-list \- List archive contents .SH SYNOPSIS .sp borg [common options] list [options] NAME [PATH...] .SH DESCRIPTION .sp This command lists the contents of an archive. .sp For more help on include/exclude patterns, see the \fIborg_patterns\fP command output. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B NAME specify the archive name .TP .B PATH paths to list; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-\-short only print file/directory names, nothing else .TP .BI \-\-format \ FORMAT specify format for file listing (default: \(dq{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}\(dq) .TP .B \-\-json\-lines Format output as JSON Lines. The form of \fB\-\-format\fP is ignored, but keys used in it are added to the JSON output. Some keys are always present. Note: JSON can only represent text. .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .UNINDENT .SH EXAMPLES .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg list root\-2016\-02\-15 drwxr\-xr\-x root root 0 Mon, 2016\-02\-15 17:44:27 . drwxrwxr\-x root root 0 Mon, 2016\-02\-15 19:04:49 bin \-rwxr\-xr\-x root root 1029624 Thu, 2014\-11\-13 00:08:51 bin/bash lrwxrwxrwx root root 0 Fri, 2015\-03\-27 20:24:26 bin/bzcmp \-> bzdiff \-rwxr\-xr\-x root root 2140 Fri, 2015\-03\-27 20:24:22 bin/bzdiff \&... $ borg list root\-2016\-02\-15 \-\-pattern \(dq\- bin/ba*\(dq drwxr\-xr\-x root root 0 Mon, 2016\-02\-15 17:44:27 . drwxrwxr\-x root root 0 Mon, 2016\-02\-15 19:04:49 bin lrwxrwxrwx root root 0 Fri, 2015\-03\-27 20:24:26 bin/bzcmp \-> bzdiff \-rwxr\-xr\-x root root 2140 Fri, 2015\-03\-27 20:24:22 bin/bzdiff \&... $ borg list archiveA \-\-format=\(dq{mode} {user:6} {group:6} {size:8d} {isomtime} {path}{extra}{NEWLINE}\(dq drwxrwxr\-x user user 0 Sun, 2015\-02\-01 11:00:00 . drwxrwxr\-x user user 0 Sun, 2015\-02\-01 11:00:00 code drwxrwxr\-x user user 0 Sun, 2015\-02\-01 11:00:00 code/myproject \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.ext \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.text \&... $ borg list archiveA \-\-pattern \(aq+ re:\e.ext$\(aq \-\-pattern \(aq\- re:^.*$\(aq \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.ext \&... $ borg list archiveA \-\-pattern \(aq+ re:.ext$\(aq \-\-pattern \(aq\- re:^.*$\(aq \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.ext \-rw\-rw\-r\-\- user user 1416192 Sun, 2015\-02\-01 11:00:00 code/myproject/file.text \&... .ft P .fi .UNINDENT .UNINDENT .SH NOTES .SS The FORMAT specifier syntax .sp The \fB\-\-format\fP option uses python\(aqs \fI\%format string syntax\fP\&. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ borg list \-\-format \(aq{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}\(aq ArchiveFoo \-rw\-rw\-r\-\- user user 1024 Thu, 2021\-12\-09 10:22:17 file\-foo \&... # {VAR:NUMBER} \- pad to NUMBER columns right\-aligned. $ borg list \-\-format \(aq{mode} {user:>6} {group:>6} {size:<8} {mtime} {path}{extra}{NL}\(aq ArchiveFoo \-rw\-rw\-r\-\- user user 1024 Thu, 2021\-12\-09 10:22:17 file\-foo \&... .ft P .fi .UNINDENT .UNINDENT .sp The following keys are always available: .INDENT 0.0 .IP \(bu 2 NEWLINE: OS dependent line separator .IP \(bu 2 NL: alias of NEWLINE .IP \(bu 2 NUL: NUL character for creating print0 / xargs \-0 like output .IP \(bu 2 SPACE: space character .IP \(bu 2 TAB: tab character .IP \(bu 2 CR: carriage return character .IP \(bu 2 LF: line feed character .UNINDENT .sp Keys available only when listing files in an archive: .INDENT 0.0 .IP \(bu 2 type: file type (file, dir, symlink, ...) .IP \(bu 2 mode: file mode (as in stat) .IP \(bu 2 uid: user id of file owner .IP \(bu 2 gid: group id of file owner .IP \(bu 2 user: user name of file owner .IP \(bu 2 group: group name of file owner .IP \(bu 2 path: file path .IP \(bu 2 target: link target for symlinks .IP \(bu 2 hlid: hard link identity (same if hardlinking same fs object) .IP \(bu 2 flags: file flags .IP \(bu 2 size: file size .IP \(bu 2 num_chunks: number of chunks in this file .IP \(bu 2 mtime: file modification time .IP \(bu 2 ctime: file change time .IP \(bu 2 atime: file access time .IP \(bu 2 isomtime: file modification time (ISO 8601 format) .IP \(bu 2 isoctime: file change time (ISO 8601 format) .IP \(bu 2 isoatime: file access time (ISO 8601 format) .IP \(bu 2 blake2b .IP \(bu 2 blake2s .IP \(bu 2 md5 .IP \(bu 2 sha1 .IP \(bu 2 sha224 .IP \(bu 2 sha256 .IP \(bu 2 sha384 .IP \(bu 2 sha3_224 .IP \(bu 2 sha3_256 .IP \(bu 2 sha3_384 .IP \(bu 2 sha3_512 .IP \(bu 2 sha512 .IP \(bu 2 xxh64: XXH64 checksum of this file (note: this is NOT a cryptographic hash!) .IP \(bu 2 archiveid: internal ID of the archive .IP \(bu 2 archivename: name of the archive .IP \(bu 2 extra: prepends {target} with \(dq \-> \(dq for soft links and \(dq link to \(dq for hard links .IP \(bu 2 health: either \(dqhealthy\(dq (file ok) or \(dqbroken\(dq (if file has all\-zero replacement chunks) .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-info(1)\fP, \fIborg\-diff(1)\fP, \fIborg\-prune(1)\fP, \fIborg\-patterns(1)\fP, \fIborg\-repo\-list(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-match-archives.10000644000076500000240000000516314716225054020141 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-MATCH-ARCHIVES" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-match-archives \- Details regarding match-archives .SH DESCRIPTION .sp The \fB\-\-match\-archives\fP option matches a given pattern against the list of all archives in the repository. It can be given multiple times. .sp The patterns can have a prefix of: .INDENT 0.0 .IP \(bu 2 name: pattern match on the archive name (default) .IP \(bu 2 aid: prefix match on the archive id (only one result allowed) .IP \(bu 2 user: exact match on the username who created the archive .IP \(bu 2 host: exact match on the hostname where the archive was created .IP \(bu 2 tags: match on the archive tags .UNINDENT .sp In case of a name pattern match, it uses pattern styles similar to the ones described by \fBborg help patterns\fP: .INDENT 0.0 .TP .B Identical match pattern, selector \fBid:\fP (default) Simple string match, must fully match exactly as given. .TP .B Shell\-style patterns, selector \fBsh:\fP Match like on the shell, wildcards like \fI*\fP and \fI?\fP work. .TP .B \fI\%Regular expressions\fP, selector \fBre:\fP Full regular expression support. This is very powerful, but can also get rather complicated. .UNINDENT .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # name match, id: style borg delete \-\-match\-archives \(aqid:archive\-with\-crap\(aq borg delete \-a \(aqid:archive\-with\-crap\(aq # same, using short option borg delete \-a \(aqarchive\-with\-crap\(aq # same, because \(aqid:\(aq is the default # name match, sh: style borg delete \-a \(aqsh:home\-kenny\-*\(aq # name match, re: style borg delete \-a \(aqre:pc[123]\-home\-(user1|user2)\-2022\-09\-.*\(aq # archive id prefix match: borg delete \-a \(aqaid:d34db33f\(aq # host or user match borg delete \-a \(aquser:kenny\(aq borg delete \-a \(aqhost:kenny\-pc\(aq # tags match borg delete \-a \(aqtags:TAG1\(aq \-a \(aqtags:TAG2\(aq .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-mount.10000644000076500000240000001456314716225054016411 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-MOUNT" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-mount \- Mount archive or an entire repository as a FUSE filesystem .SH SYNOPSIS .sp borg [common options] mount [options] MOUNTPOINT [PATH...] .SH DESCRIPTION .sp This command mounts a repository or an archive as a FUSE filesystem. This can be useful for browsing or restoring individual files. .sp When restoring, take into account that the current FUSE implementation does not support special fs flags and ACLs. .sp When mounting a repository, the top directories will be named like the archives and the directory structure below these will be loaded on\-demand from the repository when entering these directories, so expect some delay. .sp Unless the \fB\-\-foreground\fP option is given the command will run in the background until the filesystem is \fBumounted\fP\&. .sp Performance tips: .INDENT 0.0 .IP \(bu 2 when doing a \(dqwhole repository\(dq mount: do not enter archive dirs if not needed, this avoids on\-demand loading. .IP \(bu 2 only mount a specific archive, not the whole repository. .IP \(bu 2 only mount specific paths in a specific archive, not the complete archive. .UNINDENT .sp The command \fBborgfs\fP provides a wrapper for \fBborg mount\fP\&. This can also be used in fstab entries: \fB/path/to/repo /mnt/point fuse.borgfs defaults,noauto 0 0\fP .sp To allow a regular user to use fstab entries, add the \fBuser\fP option: \fB/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0\fP .sp For FUSE configuration and mount options, see the mount.fuse(8) manual page. .sp Borg\(aqs default behavior is to use the archived user and group names of each file and map them to the system\(aqs respective user and group ids. Alternatively, using \fBnumeric\-ids\fP will instead use the archived user and group ids without any mapping. .sp The \fBuid\fP and \fBgid\fP mount options (implemented by Borg) can be used to override the user and group ids of all files (i.e., \fBborg mount \-o uid=1000,gid=1000\fP). .sp The man page references \fBuser_id\fP and \fBgroup_id\fP mount options (implemented by fuse) which specify the user and group id of the mount owner (aka, the user who does the mounting). It is set automatically by libfuse (or the filesystem if libfuse is not used). However, you should not specify these manually. Unlike the \fBuid\fP and \fBgid\fP mount options which affect all files, \fBuser_id\fP and \fBgroup_id\fP affect the user and group id of the mounted (base) directory. .sp Additional mount options supported by borg: .INDENT 0.0 .IP \(bu 2 \fBversions\fP: when used with a repository mount, this gives a merged, versioned view of the files in the archives. EXPERIMENTAL, layout may change in future. .IP \(bu 2 \fBallow_damaged_files\fP: by default damaged files (where missing chunks were replaced with runs of zeros by \fBborg check \-\-repair\fP) are not readable and return EIO (I/O error). Set this option to read such files. .IP \(bu 2 \fBignore_permissions\fP: for security reasons the \fBdefault_permissions\fP mount option is internally enforced by borg. \fBignore_permissions\fP can be given to not enforce \fBdefault_permissions\fP\&. .UNINDENT .sp The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users to tweak the performance. It sets the number of cached data chunks; additional memory usage can be up to ~8 MiB times this number. The default is the number of CPU cores. .sp When the daemonized process receives a signal or crashes, it does not unmount. Unmounting in these cases could cause an active rsync or similar process to delete data unintentionally. .sp When running in the foreground, ^C/SIGINT cleanly unmounts the filesystem, but other signals or crashes do not. .SH OPTIONS .sp See \fIborg\-common(1)\fP for common options of Borg commands. .SS arguments .INDENT 0.0 .TP .B MOUNTPOINT where to mount filesystem .TP .B PATH paths to extract; patterns are supported .UNINDENT .SS optional arguments .INDENT 0.0 .TP .B \-f\fP,\fB \-\-foreground stay in foreground, do not daemonize .TP .B \-o Extra mount options .TP .B \-\-numeric\-ids use numeric user and group identifiers from archive(s) .UNINDENT .SS Archive filters .INDENT 0.0 .TP .BI \-a \ PATTERN\fR,\fB \ \-\-match\-archives \ PATTERN only consider archives matching all patterns. see \(dqborg help match\-archives\(dq. .TP .BI \-\-sort\-by \ KEYS Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp .TP .BI \-\-first \ N consider first N archives after other filters were applied .TP .BI \-\-last \ N consider last N archives after other filters were applied .TP .BI \-\-oldest \ TIMESPAN consider archives between the oldest archive\(aqs timestamp and (oldest + TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newest \ TIMESPAN consider archives between the newest archive\(aqs timestamp and (newest \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-older \ TIMESPAN consider archives older than (now \- TIMESPAN), e.g. 7d or 12m. .TP .BI \-\-newer \ TIMESPAN consider archives newer than (now \- TIMESPAN), e.g. 7d or 12m. .UNINDENT .SS Include/Exclude options .INDENT 0.0 .TP .BI \-e \ PATTERN\fR,\fB \ \-\-exclude \ PATTERN exclude paths matching PATTERN .TP .BI \-\-exclude\-from \ EXCLUDEFILE read exclude patterns from EXCLUDEFILE, one per line .TP .BI \-\-pattern \ PATTERN include/exclude paths matching PATTERN .TP .BI \-\-patterns\-from \ PATTERNFILE read include/exclude patterns from PATTERNFILE, one per line .TP .BI \-\-strip\-components \ NUMBER Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped. .UNINDENT .SH SEE ALSO .sp \fIborg\-common(1)\fP, \fIborg\-umount(1)\fP, \fIborg\-extract(1)\fP .SH AUTHOR The Borg Collective .\" Generated by docutils manpage writer. . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731799596.0 borgbackup-2.0.0b14/docs/man/borg-patterns.10000644000076500000240000003065014716225054017102 0ustar00twstaff.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "BORG-PATTERNS" 1 "2024-11-16" "" "borg backup tool" .SH NAME borg-patterns \- Details regarding patterns .SH DESCRIPTION .sp When specifying one or more file paths in a Borg command that supports patterns for the respective option or argument, you can apply the patterns described here to include only desired files and/or exclude unwanted ones. Patterns can be used .INDENT 0.0 .IP \(bu 2 for \fB\-\-exclude\fP option, .IP \(bu 2 in the file given with \fB\-\-exclude\-from\fP option, .IP \(bu 2 for \fB\-\-pattern\fP option, .IP \(bu 2 in the file given with \fB\-\-patterns\-from\fP option and .IP \(bu 2 for \fBPATH\fP arguments that explicitly support them. .UNINDENT .sp Borg always stores all file paths normalized and relative to the current recursion root. The recursion root is also named \fBPATH\fP in Borg commands like \fIborg create\fP that do a file discovery, so do not confuse the root with the \fBPATH\fP argument of e.g. \fIborg extract\fP\&. .sp Starting with Borg 1.2, paths that are matched against patterns always appear relative. If you give \fB/absolute/\fP as root, the paths going into the matcher will start with \fBabsolute/\fP\&. If you give \fB\&../../relative\fP as root, the paths will be normalized as \fBrelative/\fP\&. .sp Borg supports different pattern styles. To define a non\-default style for a specific pattern, prefix it with two characters followed by a colon \(aq:\(aq (i.e. \fBfm:path/*\fP, \fBsh:path/**\fP). .INDENT 0.0 .TP .B \fI\%Fnmatch\fP, selector \fBfm:\fP This is the default style for \fB\-\-exclude\fP and \fB\-\-exclude\-from\fP\&. These patterns use a variant of shell pattern syntax, with \(aq*\(aq matching any number of characters, \(aq?\(aq matching any single character, \(aq[...]\(aq matching any single character specified, including ranges, and \(aq[!...]\(aq matching any character not specified. For the purpose of these patterns, the path separator (backslash for Windows and \(aq/\(aq on other systems) is not treated specially. Wrap meta\-characters in brackets for a literal match (i.e. \fB[?]\fP to match the literal character \(aq?\(aq). For a path to match a pattern, the full path must match, or it must match from the start of the full path to just before a path separator. Except for the root path, paths will never end in the path separator when matching is attempted. Thus, if a given pattern ends in a path separator, a \(aq*\(aq is appended before matching is attempted. A leading path separator is always removed. .TP .B Shell\-style patterns, selector \fBsh:\fP This is the default style for \fB\-\-pattern\fP and \fB\-\-patterns\-from\fP\&. Like fnmatch patterns these are similar to shell patterns. The difference is that the pattern may include \fB**/\fP for matching zero or more directory levels, \fB*\fP for matching zero or more arbitrary characters with the exception of any path separator, \fB{}\fP containing comma\-separated alternative patterns. A leading path separator is always removed. .TP .B \fI\%Regular expressions\fP, selector \fBre:\fP Unlike shell patterns, regular expressions are not required to match the full path and any substring match is sufficient. It is strongly recommended to anchor patterns to the start (\(aq^\(aq), to the end (\(aq$\(aq) or both. Path separators (backslash for Windows and \(aq/\(aq on other systems) in paths are always normalized to a forward slash \(aq/\(aq before applying a pattern. .TP .B Path prefix, selector \fBpp:\fP This pattern style is useful to match whole sub\-directories. The pattern \fBpp:root/somedir\fP matches \fBroot/somedir\fP and everything therein. A leading path separator is always removed. .TP .B Path full\-match, selector \fBpf:\fP This pattern style is (only) useful to match full paths. This is kind of a pseudo pattern as it can not have any variable or unspecified parts \- the full path must be given. \fBpf:root/file.ext\fP matches \fBroot/file.ext\fP only. A leading path separator is always removed. .sp Implementation note: this is implemented via very time\-efficient O(1) hashtable lookups (this means you can have huge amounts of such patterns without impacting performance much). Due to that, this kind of pattern does not respect any context or order. If you use such a pattern to include a file, it will always be included (if the directory recursion encounters it). Other include/exclude patterns that would normally match will be ignored. Same logic applies for exclude. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 \fBre:\fP, \fBsh:\fP and \fBfm:\fP patterns are all implemented on top of the Python SRE engine. It is very easy to formulate patterns for each of these types which requires an inordinate amount of time to match paths. If untrusted users are able to supply patterns, ensure they cannot supply \fBre:\fP patterns. Further, ensure that \fBsh:\fP and \fBfm:\fP patterns only contain a handful of wildcards at most. .UNINDENT .UNINDENT .sp Exclusions can be passed via the command line option \fB\-\-exclude\fP\&. When used from within a shell, the patterns should be quoted to protect them from expansion. .sp The \fB\-\-exclude\-from\fP option permits loading exclusion patterns from a text file with one pattern per line. Lines empty or starting with the hash sign \(aq#\(aq after removing whitespace on both ends are ignored. The optional style selector prefix is also supported for patterns loaded from a file. Due to whitespace removal, paths with whitespace at the beginning or end can only be excluded using regular expressions. .sp To test your exclusion patterns without performing an actual backup you can run \fBborg create \-\-list \-\-dry\-run ...\fP\&. .sp Examples: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # Exclude \(aq/home/user/file.o\(aq but not \(aq/home/user/file.odt\(aq: $ borg create \-e \(aq*.o\(aq archive / # Exclude \(aq/home/user/junk\(aq and \(aq/home/user/subdir/junk\(aq but # not \(aq/home/user/importantjunk\(aq or \(aq/etc/junk\(aq: $ borg create \-e \(aqhome/*/junk\(aq archive / # Exclude the contents of \(aq/home/user/cache\(aq but not the directory itself: $ borg create \-e home/user/cache/ archive / # The file \(aq/home/user/cache/important\(aq is *not* backed up: $ borg create \-e home/user/cache/ archive / /home/user/cache/important # The contents of directories in \(aq/home\(aq are not backed up when their name # ends in \(aq.tmp\(aq $ borg create \-\-exclude \(aqre:^home/[^/]+\e.tmp/\(aq archive / # Load exclusions from file $ cat >exclude.txt <