Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,26 @@ General Public License v2 (GPLv2). See LICENSES directory or go to
-->
# Changelog
[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org)
<!-- Template
## Unreleased
### Changed
### Added
### Removed
### Fixed
-->

## [2.0.0] (Unreleased Development)

## Changed
### Changed

- **Rewritten from scratch**: Mount subsystem (backend and encryption).
Behavior is intended to remain unchanged; regressions cannot be fully ruled
out.
- Default mountpoint permissions changed from 700 to 711 ([PR#2451](https://github.com/bit-team/backintime/pull/2451))
to avoid FUSE mount failures when accessing via different user contexts.
- **Breaking**: Minimal Python version 3.13 increased
- Changelog migrated to Common Changelog standard
- Build: Changelog shipped as HTML

## Added
### Added
- Build dependency `pandoc` to convert markdown changelog into HTML

### Removed
- Command line switch `--keep-mount`
- CLI Command `decode` because of EncFS removal ([#1734](https://github.com/bit-team/backintime/issues/1734))

## [1.6.1] (2026-02-10)

### Fixed
Expand Down Expand Up @@ -1607,6 +1608,14 @@ General Public License v2 (GPLv2). See LICENSES directory or go to

- This is the first release.

<!-- Template
## Unreleased
### Changed
### Added
### Removed
### Fixed
-->

[2.0.0]: https://github.com/bit-team/backintime/releases/tag/v2.0.0
[1.6.2]: https://github.com/bit-team/backintime/releases/tag/v1.6.2
[1.6.1]: https://github.com/bit-team/backintime/releases/tag/v1.6.1
Expand Down
9 changes: 9 additions & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ path = [
SPDX-License-Identifier = "CC0-1.0"
SPDX-FileCopyrightText = "© 2008 Back In Time Team"

[[annotations]]
path = [
"myconfig*",

]
SPDX-License-Identifier = "CC0-1.0"
SPDX-FileCopyrightText = "© 2026 Back In Time Team"



[[annotations]]
path = [
Expand Down
5 changes: 5 additions & 0 deletions common/bitbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
# | Directories & files |
# |---------------------|

XDG_DATA_HOME = Path(os.environ.get(
'XDG_DATA_HOME',
os.environ.get('HOME') + '/.local/share'
))

FILENAME_CONFIG = 'config'

_DIR_DOC_PATH_BASE = Path('/') / 'usr' / 'share' / 'doc'
Expand Down
2 changes: 1 addition & 1 deletion common/bitlicense.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def _determine_licenses_dir() -> str | None:
return fp

# it might be a source repo
fp = Path.cwd().parent / 'LICENSES'
fp = Path(__file__).parent.parent / 'LICENSES'
if fp.is_dir():
return fp

Expand Down
78 changes: 55 additions & 23 deletions common/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
import config
import logger
import bitbase
from mount import MountManager, MountError
from typing import Optional
from version import __version__


def restore(cfg, snapshot_id=None, what=None, where=None, **kwargs):
def restore(cfg, snapshot_id, what, where, mount_manager, **kwargs):
if what is None:
what = input('File to restore: ')

Expand All @@ -32,23 +33,44 @@ def restore(cfg, snapshot_id=None, what=None, where=None, **kwargs):
if where:
where = os.path.abspath(os.path.expanduser(where))

snapshotsList = snapshots.listSnapshots(cfg)
snapshotsList = snapshots.listSnapshots(
cfg=cfg,
includeNewSnapshot=False,
reverse=True,
mounted_path=mount_manager.path
)

sid = selectSnapshot(
snapshotsList, cfg, snapshot_id, 'SnapshotID to restore')
snapshotsList,
cfg,
snapshot_id,
'SnapshotID to restore',
mount_manager.path
)
print('')

RestoreDialog(cfg, sid, what, where, **kwargs).run()


def remove(cfg, snapshot_ids=None, force=None):
snapshotsList = snapshots.listSnapshots(cfg)
def remove(cfg, snapshot_ids, force, mount_manager):
snapshotsList = snapshots.listSnapshots(
cfg=cfg,
includeNewSnapshot=False,
reverse=True,
mounted_path=mount_manager.path
)

if not snapshot_ids:
snapshot_ids = (None,)

sids = [
selectSnapshot(snapshotsList, cfg, sid, 'SnapshotID to remove')
selectSnapshot(
snapshotsList,
cfg,
sid,
'SnapshotID to remove',
mount_manager.path
)
for sid in snapshot_ids
]

Expand All @@ -68,9 +90,6 @@ def remove(cfg, snapshot_ids=None, force=None):


def checkConfig(cfg, crontab=True):
import mount
from exceptions import MountException

def announceTest():
print()
print(frame(test))
Expand All @@ -87,18 +106,23 @@ def errorHandler(msg):
cfg.setErrorHandler(errorHandler)
mode = cfg.snapshotsMode()

mount_manager = MountManager.create(cfg)

if cfg.SNAPSHOT_MODES[mode][0] is not None:
# preMountCheck
test = 'Run mount tests'
announceTest()
mnt = mount.Mount(cfg = cfg, tmp_mount = True)

# preMountCheck:
# - checkFuse(): checking for mount binary (gocrytpfs, encfs, ...)
# - etc pp
try:
mnt.preMountCheck(mode = mode, first_run = True)
mount_manager.validate()
# mnt.preMountCheck(mode = mode, first_run = True)

except MountException as ex:
except MountError as exc:
failed()
print(str(ex))
print(str(exc))
return False

okay()
Expand All @@ -108,21 +132,21 @@ def errorHandler(msg):
announceTest()

try:
hash_id = mnt.mount(mode=mode, check=False)
mount_manager.mount()

except MountException as ex:
except MountError as exc:
failed()
print(str(ex))
print(str(exc))
return False

okay()

test = 'Check/prepare backup path'
announceTest()
snapshots_mountpoint = cfg.get_snapshots_mountpoint(tmp_mount=True)
# snapshots_mountpoint = cfg.get_snapshots_mountpoint(tmp_mount=True)

ret = tools.validate_and_prepare_snapshots_path(
path=snapshots_mountpoint,
path=mount_manager.path,
host_user_profile=cfg.hostUserProfile(),
mode=mode,
copy_links=cfg.copyLinks(),
Expand All @@ -140,11 +164,11 @@ def errorHandler(msg):
announceTest()

try:
mnt.umount(hash_id=hash_id)
mount_manager.umount()

except MountException as ex:
except MountError as exc:
failed()
print(str(ex))
print(str(exc))
return False

okay()
Expand Down Expand Up @@ -174,7 +198,12 @@ def errorHandler(msg):
return True


def selectSnapshot(snapshotsList, cfg, snapshot_id=None, msg='SnapshotID'):
def selectSnapshot(snapshotsList,
cfg,
snapshot_id=None,
msg='SnapshotID',
mounted_path=None
):
"""
check if given snapshot is valid. If not print a list of all
snapshots and ask to choose one
Expand All @@ -184,7 +213,10 @@ def selectSnapshot(snapshotsList, cfg, snapshot_id=None, msg='SnapshotID'):
if not snapshot_id is None:

try:
sid = snapshots.SID(snapshot_id, cfg)
sid = snapshots.SID(
date=snapshot_id,
cfg=cfg,
mounted_path=mounted_path)

if sid in snapshotsList:
return sid
Expand Down
Loading