dar-backup 0.6.9__py3-none-any.whl → 0.6.11__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dar_backup/__about__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.6.9"
1
+ __version__ = "0.6.11"
dar_backup/dar_backup.py CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
  import argparse
4
- import datetime
5
4
  import filecmp
6
5
 
7
6
  import os
@@ -81,30 +80,31 @@ def generic_backup(type: str, command: List[str], backup_file: str, backup_defin
81
80
  raise BackupError(f"Unexpected error during backup: {e}") from e
82
81
 
83
82
 
84
- # Function to recursively find <File> tags and build their full paths
85
- def find_files_with_paths(element: ET, current_path=""):
83
+
84
+ def find_files_with_paths(xml_root: ET.Element):
86
85
  """
87
- Recursively finds files within a directory element and returns a list of file paths with their sizes.
86
+ Finds files within an XML element and returns a list of file paths with their sizes.
88
87
 
89
88
  Args:
90
- element (Element): The directory element to search within.
91
- current_path (str, optional): The current path of the directory element. Defaults to "".
89
+ xml_root (Element): The root element of the XML.
92
90
 
93
91
  Returns:
94
92
  list: A list of tuples containing file paths and their sizes.
95
93
  """
96
- logger.debug(f"Recursively generate list of tuples with file paths and sizes for File elements in dar xml output")
94
+ logger.debug("Generating list of tuples with file paths and sizes for File elements in dar xml output")
97
95
  files = []
98
- if element.tag == "Directory":
99
- current_path = f"{current_path}/{element.get('name')}"
100
- for child in element:
101
- if child.tag == "File":
102
- file_path = (f"{current_path}/{child.get('name')}", child.get('size')) # tuple (filepath, size)
96
+ current_path = []
97
+
98
+ for elem in xml_root.iter():
99
+ if elem.tag == "directory":
100
+ current_path.append(elem.get('name'))
101
+ elif elem.tag == "file":
102
+ file_path = ("/".join(current_path + [elem.get('name')]), elem.get('size'))
103
103
  files.append(file_path)
104
- elif child.tag == "Directory":
105
- files.extend(find_files_with_paths(child, current_path))
106
- return files
104
+ elif elem.tag == "directory" and elem.get('name') in current_path:
105
+ current_path.pop()
107
106
 
107
+ return files
108
108
 
109
109
 
110
110
  def find_files_between_min_and_max_size(backed_up_files: list[(str, str)], config_settings: ConfigSettings):
@@ -238,20 +238,20 @@ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_di
238
238
  restore_dir (str): The directory where the backup should be restored to.
239
239
  selection (str, optional): A selection criteria to restore specific files or directories. Defaults to None.
240
240
  """
241
- backup_file = os.path.join(config_settings.backup_dir, backup_name)
242
- command = ['dar', '-x', backup_file, '-Q', '-D']
243
- if restore_dir:
244
- if not os.path.exists(restore_dir):
245
- os.makedirs(restore_dir)
246
- command.extend(['-R', restore_dir])
247
- else:
248
- raise RestoreError("Restore directory ('-R <dir>') not specified")
249
- if selection:
250
- selection_criteria = shlex.split(selection)
251
- command.extend(selection_criteria)
252
- command.extend(['-B', darrc, 'restore-options']) # the .darrc `restore-options` section
253
- logger.info(f"Running restore command: {' '.join(map(shlex.quote, command))}")
254
241
  try:
242
+ backup_file = os.path.join(config_settings.backup_dir, backup_name)
243
+ command = ['dar', '-x', backup_file, '-Q', '-D']
244
+ if restore_dir:
245
+ if not os.path.exists(restore_dir):
246
+ os.makedirs(restore_dir)
247
+ command.extend(['-R', restore_dir])
248
+ else:
249
+ raise RestoreError("Restore directory ('-R <dir>') not specified")
250
+ if selection:
251
+ selection_criteria = shlex.split(selection)
252
+ command.extend(selection_criteria)
253
+ command.extend(['-B', darrc, 'restore-options']) # the .darrc `restore-options` section
254
+ logger.info(f"Running restore command: {' '.join(map(shlex.quote, command))}")
255
255
  process = run_command(command, config_settings.command_timeout_secs)
256
256
  if process.returncode == 0:
257
257
  logger.info(f"Restore completed successfully to: '{restore_dir}'")
@@ -260,6 +260,9 @@ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_di
260
260
  raise RestoreError(str(process))
261
261
  except subprocess.CalledProcessError as e:
262
262
  raise RestoreError(f"Restore command failed: {e}") from e
263
+ except OSError as e:
264
+ logger.error(f"Failed to create restore directory: {e}")
265
+ raise RestoreError("Could not create restore directory")
263
266
  except Exception as e:
264
267
  raise RestoreError(f"Unexpected error during restore: {e}") from e
265
268
 
@@ -285,7 +288,7 @@ def get_backed_up_files(backup_name: str, backup_dir: str):
285
288
  # Parse the XML data
286
289
  root = ET.fromstring(process.stdout)
287
290
  output = None # help gc
288
- # Extract full paths and file size for all <File> elements
291
+ # Extract full paths and file size for all <file> elements
289
292
  file_paths = find_files_with_paths(root)
290
293
  root = None # help gc
291
294
  logger.trace(str(process))
@@ -624,6 +627,14 @@ def main():
624
627
  else:
625
628
  logger.error(f"Supplied .darrc: '{args.darrc}' does not exist or is not a file")
626
629
 
630
+ if not args.config_file:
631
+ logger.error(f"Config file not specified, exiting")
632
+ sys.exit(1)
633
+
634
+ if not os.path.exists(args.config_file):
635
+ logger.error(f"Config file {args.config_file} does not exist.")
636
+ sys.exit(1)
637
+
627
638
 
628
639
  try:
629
640
  start_time=int(time())
dar_backup/util.py CHANGED
@@ -14,6 +14,7 @@ import os
14
14
  import re
15
15
  import subprocess
16
16
  import shlex
17
+ import shutil
17
18
  import sys
18
19
  import threading
19
20
  import traceback
@@ -123,10 +124,113 @@ def _stream_reader(pipe, log_func, output_accumulator: List[str]):
123
124
  log_func(stripped_line) # Log the output in real time
124
125
 
125
126
 
127
+
126
128
  def run_command(command: List[str], timeout: int = 30) -> CommandResult:
127
129
  """
128
130
  Executes a given command via subprocess, logs its output in real time, and returns the result.
129
131
 
132
+ Args:
133
+ command (list): The command to be executed, represented as a list of strings.
134
+ timeout (int): The maximum time in seconds to wait for the command to complete. Defaults to 30 seconds.
135
+
136
+ Returns:
137
+ A CommandResult NamedTuple with the following properties:
138
+ - process: subprocess.CompletedProcess
139
+ - stdout: str: The full standard output of the command.
140
+ - stderr: str: The full standard error of the command.
141
+ - returncode: int: The return code of the command.
142
+ - timeout: int: The timeout value in seconds used to run the command.
143
+ - command: list[str]: The command executed.
144
+
145
+ Logs:
146
+ - Logs standard output (`stdout`) in real-time at the INFO log level.
147
+ - Logs standard error (`stderr`) in real-time at the ERROR log level.
148
+
149
+ Raises:
150
+ subprocess.TimeoutExpired: If the command execution times out (see `timeout` parameter).
151
+ Exception: If other exceptions occur during command execution.
152
+ FileNotFoundError: If the command is not found.
153
+
154
+ Notes:
155
+ - While the command runs, its `stdout` and `stderr` streams are logged in real-time.
156
+ - The returned `stdout` and `stderr` capture the complete output, even though the output is also logged.
157
+ - The command is forcibly terminated if it exceeds the specified timeout.
158
+ """
159
+ stdout_lines = [] # To accumulate stdout
160
+ stderr_lines = [] # To accumulate stderr
161
+ process = None # Track the process for cleanup
162
+ stdout_thread = None
163
+ stderr_thread = None
164
+
165
+ try:
166
+ # Check if the command exists before executing
167
+ if not shutil.which(command[0]):
168
+ raise FileNotFoundError(f"Command not found: {command[0]}")
169
+
170
+ logger.debug(f"Running command: {command}")
171
+ process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
172
+
173
+ # Start threads to read and log stdout and stderr
174
+ stdout_thread = threading.Thread(target=_stream_reader, args=(process.stdout, logger.info, stdout_lines))
175
+ stderr_thread = threading.Thread(target=_stream_reader, args=(process.stderr, logger.error, stderr_lines))
176
+
177
+ stdout_thread.start()
178
+ stderr_thread.start()
179
+
180
+ # Wait for process to complete or timeout
181
+ process.wait(timeout=timeout)
182
+
183
+ except FileNotFoundError as e:
184
+ logger.error(f"Command not found: {command[0]}")
185
+ return CommandResult(
186
+ process=None,
187
+ stdout="",
188
+ stderr=str(e),
189
+ returncode=127,
190
+ timeout=timeout,
191
+ command=command
192
+ )
193
+ except subprocess.TimeoutExpired:
194
+ if process:
195
+ process.terminate()
196
+ logger.error(f"Command: '{command}' timed out and was terminated.")
197
+ raise
198
+ except Exception as e:
199
+ logger.error(f"Error running command: {command}", exc_info=True)
200
+ raise
201
+ finally:
202
+ # Ensure threads are joined to clean up (only if they were started)
203
+ if stdout_thread and stdout_thread.is_alive():
204
+ stdout_thread.join()
205
+ if stderr_thread and stderr_thread.is_alive():
206
+ stderr_thread.join()
207
+
208
+ # Ensure process streams are closed
209
+ if process and process.stdout:
210
+ process.stdout.close()
211
+ if process and process.stderr:
212
+ process.stderr.close()
213
+
214
+ # Combine captured stdout and stderr lines into single strings
215
+ stdout = "\n".join(stdout_lines)
216
+ stderr = "\n".join(stderr_lines)
217
+
218
+ # Build the result object
219
+ result = CommandResult(
220
+ process=process,
221
+ stdout=stdout,
222
+ stderr=stderr,
223
+ returncode=process.returncode,
224
+ timeout=timeout,
225
+ command=command
226
+ )
227
+ logger.debug(f"Command result: {result}")
228
+ return result
229
+
230
+ def run_command2(command: List[str], timeout: int = 30) -> CommandResult:
231
+ """
232
+ Executes a given command via subprocess, logs its output in real time, and returns the result.
233
+
130
234
  Args:
131
235
  command (list): The command to be executed, represented as a list of strings.
132
236
  timeout (int): The maximum time in seconds to wait for the command to complete. Defaults to 30 seconds.
@@ -158,6 +262,10 @@ def run_command(command: List[str], timeout: int = 30) -> CommandResult:
158
262
  process = None # Track the process for cleanup
159
263
 
160
264
  try:
265
+ # Check if the command exists before executing
266
+ if not shutil.which(command[0]):
267
+ raise FileNotFoundError(f"Command not found: {command[0]}")
268
+
161
269
  logger.debug(f"Running command: {command}")
162
270
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
163
271
 
@@ -171,6 +279,16 @@ def run_command(command: List[str], timeout: int = 30) -> CommandResult:
171
279
  # Wait for process to complete or timeout
172
280
  process.wait(timeout=timeout)
173
281
 
282
+ except FileNotFoundError as e:
283
+ logger.error(f"Command not found: {command[0]}")
284
+ return CommandResult(
285
+ process=None,
286
+ stdout="",
287
+ stderr=str(e),
288
+ returncode=127,
289
+ timeout=timeout,
290
+ command=command
291
+ )
174
292
  except subprocess.TimeoutExpired:
175
293
  if process:
176
294
  process.terminate()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dar-backup
3
- Version: 0.6.9
3
+ Version: 0.6.11
4
4
  Summary: A script to do full, differential and incremental backups using dar. Some files are restored from the backups during verification, after which par2 redundancy files are created. The script also has a cleanup feature to remove old backups and par2 files.
5
5
  Project-URL: Homepage, https://github.com/per2jensen/dar-backup/tree/main/v2
6
6
  Project-URL: Changelog, https://github.com/per2jensen/dar-backup/blob/main/v2/Changelog.md
@@ -690,71 +690,123 @@ Classifier: Topic :: System :: Archiving :: Backup
690
690
  Requires-Python: >=3.9
691
691
  Description-Content-Type: text/markdown
692
692
 
693
- # Full, differential or incremental backups using 'dar'
693
+ # Full, differential or incremental backups using 'dar'
694
694
 
695
- The wonderful 'dar' [Disk Archiver] (https://github.com/Edrusb/DAR) is used for
695
+ The wonderful 'dar' [Disk Archiver](https://github.com/Edrusb/DAR) is used for
696
696
  the heavy lifting, together with the par2 suite in these scripts.
697
697
 
698
-
699
- # My use case
700
-
701
- I have cloud storage mounted on a directory within my home dir. The filesystem is [FUSE based](https://www.kernel.org/doc/html/latest/filesystems/fuse.html), which gives it a few special features
702
- - a non-privileged user (me :-)) can perform a mount
703
- - a privileged user cannot look into the filesystem --> a backup script running as root is not suitable
698
+ ## Table of Contents
699
+
700
+ - [Full, differential or incremental backups using 'dar'](#full-differential-or-incremental-backups-using-dar)
701
+ - [My use case](#my-use-case)
702
+ - [License](#license)
703
+ - [Status](#status)
704
+ - [Breaking change in version 0.6.0](#breaking-change-in-version-060)
705
+ - [Homepage - Github](#homepage---github)
706
+ - [Requirements](#requirements)
707
+ - [Config file](#config-file)
708
+ - [How to install & run](#how-to-run)
709
+ - [1 - config file](#1---config-file)
710
+ - [2 - backup definitions](#2---backup-definitions)
711
+ - [3 - installation](#3---installation)
712
+ - [4 - generate catalog databases](#4---generate-catalog-databases)
713
+ - [5 - do FULL backups](#5---do-full-backups)
714
+ - [6 - deactivate venv](#6---deactivate-venv)
715
+ - [.darrc](#darrc)
716
+ - [Systemctl examples](#systemctl-examples)
717
+ - [Service: dar-back --incremental-backup](#service-dar-back---incremental-backup)
718
+ - [Timer: dar-back --incremental-backup](#timer-dar-back---incremental-backup)
719
+ - [List contents of an archive](#list-contents-of-an-archive)
720
+ - [dar file selection examples](#dar-file-selection-exmaples)
721
+ - [Select a directory](#select-a-directory)
722
+ - [Select file dates in the directory](#select-file-dates-in-the-directory)
723
+ - [Exclude .xmp files from that date](#exclude-xmp-files-from-that-date)
724
+ - [Restoring](#restoring)
725
+ - [Default location for restores](#default-location-for-restores)
726
+ - [--restore-dir option](#--restore-dir-option)
727
+ - [A single file](#a-single-file)
728
+ - [A directory](#a-directory)
729
+ - [.NEF from a specific date](#nef-from-a-specific-date)
730
+ - [Restore test fails with exit code 4](#restore-test-fails-with-exit-code-4)
731
+ - [Restore test fails with exit code 5](#restore-test-fails-with-exit-code-5)
732
+ - [Par2](#par2)
733
+ - [Par2 to verify/repair](#par2-to-verifyrepair)
734
+ - [Par2 create redundancy files](#par2-create-redundancy-files)
735
+ - [Points of interest](#points-of-interest)
736
+ - [Merge FULL with DIFF, creating new FULL](#merge-full-with-diff-creating-new-full)
737
+ - [dar manager databases](#dar-manager-databases)
738
+ - [Performance tip due to par2](#performance-tip-due-to-par2)
739
+ - [.darrc sets -vd -vf (since v0.6.4)](#darrc-sets--vd--vf-since-v064)
740
+ - [Reference](#reference)
741
+ - [dar-backup.py](#dar-backuppy)
742
+ - [manager.py](#managerpy)
743
+ - [cleanup.py](#cleanuppy)
744
+ - [clean-log.py](#clean-logpy)
745
+
746
+ ## My use case
747
+
748
+ I have cloud storage mounted on a directory within my home dir. The filesystem is [FUSE based](https://www.kernel.org/doc/html/latest/filesystems/fuse.html), which gives it a few special features
749
+
750
+ - a non-privileged user (me :-)) can perform a mount
751
+ - a privileged user cannot look into the filesystem --> a backup script running as root is not suitable
704
752
 
705
753
  I needed the following:
706
- - Backup my cloud storage to something local (cloud is convenient, but I want control over my backups)
707
- - Backup primarily photos, video and different types of documents
708
- - Have a simple non-complicated way of restoring, possibly years into the future. 'dar' fits that scenario with a single statically linked binary (kept with the archives). There is no need install/configure anything - restoring is simple and works well.
709
- - During backup archives must be tested and a restore test (however small) performed
710
- - Archives stored on a server with a reliable file system (easy to mount a directory over sshfs)
711
- - Easy to verify archive's integrity, after being moved around.
754
+
755
+ - Backup my cloud storage to something local (cloud is convenient, but I want control over my backups)
756
+ - Backup primarily photos, video and different types of documents
757
+ - Have a simple non-complicated way of restoring, possibly years into the future. 'dar' fits that scenario with a single statically linked binary (kept with the archives). There is no need install/configure anything - restoring is simple and works well.
758
+ - During backup archives must be tested and a restore test (however small) performed
759
+ - Archives stored on a server with a reliable file system (easy to mount a directory over sshfs)
760
+ - Easy to verify archive's integrity, after being moved around.
712
761
 
713
762
  I do not need the encryption features of dar, as all storage is already encrypted.
714
-
715
763
 
716
- # License
764
+ ## License
717
765
 
718
766
  These scripts are licensed under the GPLv3 license.
719
767
  Read more here: https://www.gnu.org/licenses/gpl-3.0.en.html, or have a look at the ["LICENSE"](https://github.com/per2jensen/dar-backup/blob/main/LICENSE) file in this repository.
720
768
 
769
+ ## Status
721
770
 
722
- # Status
723
771
  As of August 8, 2024 I am using the alpha versions of `dar-backup` (alpha-0.5.9 onwards) in my automated backup routine.
724
772
 
725
773
  As of February 13, 2025, I have changed the status from alpha --> beta, as the featureset is in place and the alphas have worked well for a very long time.
726
774
 
727
-
728
- **Breaking change in version 0.6.0**
775
+ ### Breaking change in version 0.6.0
729
776
 
730
777
  Version 0.6.0 and forwards requires the config variable *COMMAND_TIMEOUT_SECS* in the config file.
731
778
 
732
- # Homepage - Github
733
- This 'dar-backup' package lives at: https://github.com/per2jensen/dar-backup
779
+ ## Homepage - Github
780
+
781
+ This 'dar-backup' package lives at: https://github.com/per2jensen/dar-backup/tree/main/v2
734
782
 
735
783
  This python version is v2 of dar-backup, the first is made in bash.
736
784
 
737
- # Requirements
738
- - dar
739
- - par2
740
- - python3 :-)
785
+ ## Requirements
786
+
787
+ - dar
788
+ - par2
789
+ - python3 :-)
741
790
 
742
791
  On Ubuntu, install the requirements this way:
743
- ````
792
+
793
+ ```` bash
744
794
  sudo apt install dar par2 python3
745
- ````
795
+ ````
746
796
 
747
- # Config file
797
+ ## Config file
748
798
 
749
799
  The default configuration is expected here: ~/.config/dar-backup/dar-backup.conf
750
800
 
751
- # How to run
801
+ ## How to run
802
+
803
+ ### 1 - config file
752
804
 
753
- ## 1
754
805
  Config file default location is $HOME/.config/dar-backup/dar-backup.conf
755
806
 
756
807
  Example:
757
- ````
808
+
809
+ ```` code
758
810
  [MISC]
759
811
  LOGFILE_LOCATION=/home/user/dar-backup.log
760
812
  MAX_SIZE_VERIFICATION_MB = 20
@@ -791,9 +843,10 @@ ENABLED = True
791
843
  # SCRIPT_2 = <something>
792
844
  # ...
793
845
 
794
- ````
846
+ ````
847
+
848
+ ### 2 - backup definitions
795
849
 
796
- ## 2
797
850
  Put your backup definitions in the directory $BACKUP.D_DIR (defined in the config file)
798
851
 
799
852
  The name of the file is the `backup definition` name.
@@ -803,7 +856,8 @@ Make as many backup definitions as you need. Run them all in one go, or run one
803
856
  The `dar` [documentation](http://dar.linux.free.fr/doc/man/dar.html#COMMANDS%20AND%20OPTIONS) has good information on file selection.
804
857
 
805
858
  Example of backup definition for a home directory
806
- ````
859
+
860
+ ```` code
807
861
 
808
862
  # Switch to ordered selection mode, which means that the following
809
863
  # options will be considered top to bottom
@@ -817,7 +871,7 @@ Example of backup definition for a home directory
817
871
  # if you want to take a backup of /home/user/Documents only, uncomment next line
818
872
  # -g Documents
819
873
 
820
- # Some directories to exclude below the Root dir
874
+ # Some directories to exclude below the Root dir (here Root directory is `/home/user` as set in the -R option)
821
875
  -P mnt
822
876
  -P tmp
823
877
  -P .cache
@@ -836,131 +890,93 @@ Example of backup definition for a home directory
836
890
  # size of each slice in the archive
837
891
  --slice 10G
838
892
 
839
- # see https://github.com/per2jensen/dar-backup?tab=readme-ov-file#restore-test-exit-code-4
840
- --comparison-field=ignore-owner
841
893
 
842
894
  # bypass directores marked as cache directories
843
895
  # http://dar.linux.free.fr/doc/Features.html
844
896
  --cache-directory-tagging
845
- ````
897
+ ````
846
898
 
899
+ ### 3 - installation
847
900
 
848
- ## 3
849
901
  Installation is currently in a venv. These commands are installed in the venv:
902
+
850
903
  - dar-back
851
904
  - cleanup
852
905
  - manager
906
+ - clean-log
853
907
 
854
908
  To install, create a venv and run pip:
855
- ````
909
+
910
+ ```` bash
856
911
  mkdir $HOME/tmp
857
912
  cd $HOME/tmp
858
913
  python3 -m venv venv # create the virtual environment
859
914
  . venv/bin/activate # activate the virtual env
860
915
  pip install dar-backup # run pip to install `dar-backup`
861
- ````
862
-
916
+ ````
863
917
 
864
918
  I have an alias in ~/.bashrc pointing to my venv:
865
- ````
919
+
920
+ ```` bash
866
921
  alias db=". ~/tmp/venv/bin/activate; dar-backup -v"
867
- ````
922
+ ````
868
923
 
869
924
  Typing `db` at the command line gives this
870
- ````
925
+
926
+ ```` bash
871
927
  (venv) user@machine:~$ db
872
- dar-backup 0.5.17
928
+ dar-backup 0.6.9
873
929
  dar-backup.py source code is here: https://github.com/per2jensen/dar-backup
874
930
  Licensed under GNU GENERAL PUBLIC LICENSE v3, see the supplied file "LICENSE" for details.
875
931
  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
876
932
  See section 15 and section 16 in the supplied "LICENSE" file.
877
933
  ````
878
934
 
879
- `dar-backup -h` gives the usage output:
880
- ````
881
- usage: dar-backup [-h] [-F] [-D] [-I] [-d BACKUP_DEFINITION]
882
- [--alternate-reference-archive ALTERNATE_REFERENCE_ARCHIVE] [-c CONFIG_FILE] [--darrc DARRC]
883
- [--examples] [-l] [--list-contents LIST_CONTENTS] [--selection SELECTION] [-r RESTORE]
884
- [--restore-dir RESTORE_DIR] [--verbose] [--log-level LOG_LEVEL] [--log-stdout]
885
- [--do-not-compare] [-v]
886
-
887
- Backup and verify using dar backup definitions.
888
-
889
- options:
890
- -h, --help show this help message and exit
891
- -F, --full-backup Perform a full backup.
892
- -D, --differential-backup
893
- Perform differential backup.
894
- -I, --incremental-backup
895
- Perform incremental backup.
896
- -d BACKUP_DEFINITION, --backup-definition BACKUP_DEFINITION
897
- Specific 'recipe' to select directories and files.
898
- --alternate-reference-archive ALTERNATE_REFERENCE_ARCHIVE
899
- DIFF or INCR compared to specified archive.
900
- -c CONFIG_FILE, --config-file CONFIG_FILE
901
- Path to 'dar-backup.conf'
902
- --darrc DARRC Optional path to .darrc
903
- --examples Examples of using dar-backup.py.
904
- -l, --list List available archives.
905
- --list-contents LIST_CONTENTS
906
- List the contents of the specified archive.
907
- --selection SELECTION
908
- dar file selection for listing/restoring specific files/directories.
909
- -r RESTORE, --restore RESTORE
910
- Restore specified archive.
911
- --restore-dir RESTORE_DIR
912
- Directory to restore files to.
913
- --verbose Print various status messages to screen
914
- --log-level LOG_LEVEL
915
- `debug` or `trace`
916
- --log-stdout also print log messages to stdout
917
- --do-not-compare do not compare restores to file system
918
- -v, --version Show version and license information.
919
- ````
920
-
921
- ## 4
935
+ ### 4 - generate catalog databases
936
+
922
937
  Generate the archive catalog database(s).
923
938
  `dar-backup` expects the catalog databases to be in place, it does not automatically create them (by design)
924
939
 
925
- ````
926
- manager --create-db --config-file <path to config file> --log-level debug --log-stdout
927
- ````
928
-
929
-
940
+ ```` bash
941
+ manager --create-db
942
+ ````
930
943
 
944
+ ### 5 - do FULL backups
931
945
 
932
- ## 5
933
946
  You are ready to do backups of all your backup definitions, if your backup definitions are
934
947
  in place in BACKUP.D_DIR (see config file)
935
- ````
948
+
949
+ ```` bash
936
950
  dar-backup --full-backup
937
- ````
951
+ ````
952
+
938
953
  If you want to see dar-backup's log entries in the terminal, use the `--log-stdout` option. This is also useful if dar-backup is started by systemd.
939
954
 
940
955
  If you want more log messages, use the `--log-level debug` option.
941
956
 
942
-
943
957
  If you want a backup of a single definition, use the `-d <backup definition>` option. The definition's name is the filename of the definition in the `backup.d` config directory.
944
- ````
958
+
959
+ ```` bash
945
960
  dar-backup --full-backup -d <your backup definition>
946
961
  ````
947
962
 
948
- ## 6
963
+ ### 6 - deactivate venv
949
964
 
950
- Deactivate the virtual environment
951
- ````
965
+ Deactivate the virtual environment (venv)
966
+
967
+ ```` bash
952
968
  deactivate
953
969
  ````
954
970
 
971
+ ## .darrc
955
972
 
956
- # .darrc
957
973
  The package includes a default `.darrc` file which configures `dar`.
958
974
 
959
975
  You can override the default `.darrc` using the `--darrc` option.
960
976
 
961
977
  The default `.darrc` contents are as follows:
962
978
 
963
- ````
979
+ ```` code
964
980
  # .darrc configuration file for `dar` as used by the `dar-backup` script.
965
981
  # `dar-backup` lives here: https://github.com/per2jensen/dar-backup
966
982
 
@@ -998,19 +1014,21 @@ verbose:
998
1014
  # -va
999
1015
 
1000
1016
 
1001
- extract:
1017
+ restore-options:
1002
1018
  # don't restore File Specific Attributes
1003
1019
  #--fsa-scope none
1004
1020
 
1005
1021
  # ignore owner, useful when used by a non-privileged user
1006
1022
  --comparison-field=ignore-owner
1007
1023
 
1024
+
1025
+ # Exclude specific file types from compression
1026
+ compress-exclusion:
1027
+
1008
1028
  # First setting case insensitive mode on:
1009
1029
  -an
1010
1030
  -ag
1011
1031
 
1012
- # Exclude specific file types from compression
1013
- compress-exclusion:
1014
1032
  -Z "*.gz"
1015
1033
  -Z "*.bz2"
1016
1034
  -Z "*.xz"
@@ -1081,14 +1099,15 @@ compress-exclusion:
1081
1099
  -acase
1082
1100
  ````
1083
1101
 
1084
- # Systemctl examples
1102
+ ## Systemctl examples
1103
+
1085
1104
  I have dar-backup scheduled to run via systemd --user settings.
1086
1105
 
1087
1106
  The files are located in: ~/.config/systemd/user
1088
1107
 
1089
1108
  Once the .service and .timer files are in place, timers must be enabled and started.
1090
1109
 
1091
- ````
1110
+ ```` bash
1092
1111
  systemctl --user enable dar-inc-backup.timer
1093
1112
  systemctl --user start dar-inc-backup.timer
1094
1113
  systemctl --user daemon-reload
@@ -1096,15 +1115,15 @@ systemctl --user daemon-reload
1096
1115
 
1097
1116
  Verify your timers are set up as you want:
1098
1117
 
1099
- ````
1118
+ ```` bash
1100
1119
  systemctl --user list-timers
1101
1120
  ````
1102
1121
 
1103
-
1104
1122
  ## Service: dar-back --incremental-backup
1105
1123
 
1106
1124
  File: dar-inc-backup.service
1107
- ````
1125
+
1126
+ ```` code
1108
1127
  [Unit]
1109
1128
  Description=dar-backup INC
1110
1129
  StartLimitIntervalSec=120
@@ -1119,7 +1138,8 @@ ExecStart=/bin/bash -c '. /home/user/programmer/dar-backup.py/venv/bin/activate
1119
1138
  ## Timer: dar-back --incremental-backup
1120
1139
 
1121
1140
  File: dar-inc-backup.timer
1122
- ````
1141
+
1142
+ ```` code
1123
1143
  [Unit]
1124
1144
  Description=dar-backup INC timer
1125
1145
 
@@ -1131,15 +1151,17 @@ Persistent=true
1131
1151
  WantedBy=timers.target
1132
1152
  ````
1133
1153
 
1154
+ ## list contents of an archive
1134
1155
 
1135
- # list contents of an archive
1136
- ```
1156
+ ```` bash
1137
1157
  . <the virtual evn>/bin/activate
1138
1158
  dar-backup --list-contents example_FULL_2024-06-23 --selection "-X '*.xmp' -I '*2024-06-16*' -g home/pj/tmp/LUT-play"
1139
1159
  deactivate
1140
- ```
1160
+ ````
1161
+
1141
1162
  gives
1142
- ```
1163
+
1164
+ ``` code
1143
1165
  [Data ][D][ EA ][FSA][Compr][S]| Permission | User | Group | Size | Date | filename
1144
1166
  --------------------------------+------------+-------+-------+---------+-------------------------------+------------
1145
1167
  [Saved][-] [-L-][ 0%][ ] drwxr-xr-x root root 113 Mio Sat May 11 16:16:48 2024 home
@@ -1149,17 +1171,17 @@ gives
1149
1171
  [Saved][ ] [-L-][ 0%][X] -rw-rw-r-- pj pj 49 Mio Sun Jun 16 12:52:22 2024 home/pj/tmp/LUT-play/2024-06-16_12:52:22,15.NEF
1150
1172
  ```
1151
1173
 
1174
+ ## dar file selection exmaples
1152
1175
 
1176
+ ### select a directory
1153
1177
 
1154
-
1155
- # dar file selection exmaples
1156
-
1157
- ## select a directory
1158
- ```
1178
+ ``` bash
1159
1179
  dar -l /tmp/example_FULL_2024-06-23 -g home/pj/tmp/LUT-play
1160
1180
  ```
1181
+
1161
1182
  gives
1162
- ```
1183
+
1184
+ ```` code
1163
1185
  [Data ][D][ EA ][FSA][Compr][S]| Permission | User | Group | Size | Date | filename
1164
1186
  --------------------------------+------------+-------+-------+---------+-------------------------------+------------
1165
1187
  [Saved][-] [-L-][ 0%][ ] drwxr-xr-x root root 113 Mio Sat May 11 16:16:48 2024 home
@@ -1186,15 +1208,17 @@ gives
1186
1208
  [Saved][ ] [-L-][ 92%][ ] -rw-rw-r-- pj pj 24 kio Sat Jun 22 21:51:15 2024 home/pj/tmp/LUT-play/2024-06-16_12:52:22,15_16.NEF.xmp
1187
1209
  [Saved][ ] [-L-][ 92%][ ] -rw-rw-r-- pj pj 24 kio Sat Jun 22 21:50:48 2024 home/pj/tmp/LUT-play/2024-06-16_12:52:22,15_17.NEF.xmp
1188
1210
  [Saved][ ] [-L-][ 92%][ ] -rw-rw-r-- pj pj 24 kio Sat Jun 22 21:50:19 2024 home/pj/tmp/LUT-play/2024-06-16_12:52:22,15_18.NEF.xmp
1189
- ```
1211
+ ````
1190
1212
 
1213
+ ### select file dates in the directory:
1191
1214
 
1192
- ## select file dates in the directory:
1193
- ```
1215
+ ``` bash
1194
1216
  dar -l /tmp/example_FULL_2024-06-23 -I '*2024-06-16*' -g home/pj/tmp/LUT-play
1195
1217
  ```
1218
+
1196
1219
  gives
1197
- ```
1220
+
1221
+ ``` code
1198
1222
  [Data ][D][ EA ][FSA][Compr][S]| Permission | User | Group | Size | Date | filename
1199
1223
  --------------------------------+------------+-------+-------+---------+-------------------------------+------------
1200
1224
  [Saved][-] [-L-][ 0%][ ] drwxr-xr-x root root 113 Mio Sat May 11 16:16:48 2024 home
@@ -1223,12 +1247,16 @@ gives
1223
1247
  [Saved][ ] [-L-][ 92%][ ] -rw-rw-r-- pj pj 24 kio Sat Jun 22 21:50:19 2024 home/pj/tmp/LUT-play/2024-06-16_12:52:22,15_18.NEF.xmp
1224
1248
  ```
1225
1249
 
1226
- ## exclude .xmp files from that date
1227
- ```
1250
+ ### exclude .xmp files from that date
1251
+
1252
+ ``` bash
1228
1253
  dar -l /tmp/example_FULL_2024-06-23 -X '*.xmp' -I '*2024-06-16*' -g home/pj/tmp/LUT-play
1254
+
1229
1255
  ```
1256
+
1230
1257
  gives
1231
- ```
1258
+
1259
+ ``` code
1232
1260
  [Data ][D][ EA ][FSA][Compr][S]| Permission | User | Group | Size | Date | filename
1233
1261
  --------------------------------+------------+-------+-------+---------+-------------------------------+------------
1234
1262
  [Saved][-] [-L-][ 0%][ ] drwxr-xr-x root root 113 Mio Sat May 11 16:16:48 2024 home
@@ -1240,16 +1268,16 @@ gives
1240
1268
 
1241
1269
  Nice :-)
1242
1270
 
1271
+ ## Restoring
1243
1272
 
1273
+ ### default location for restores
1244
1274
 
1245
- # Restoring
1246
-
1247
- ## default location for restores
1248
1275
  dar-backup will use the TEST_RESTORE_DIR location as the Root for restores, if the --restore-dir option has not been supplied.
1249
1276
 
1250
1277
  See example below to see where files are restored to.
1251
1278
 
1252
- ## --restore-dir option
1279
+ ### --restore-dir option
1280
+
1253
1281
  When the --restore-dir option is used for restoring, a directory must be supplied.
1254
1282
 
1255
1283
  The directory supplied functions as the Root of the restore operation.
@@ -1257,41 +1285,200 @@ The directory supplied functions as the Root of the restore operation.
1257
1285
  **Example**:
1258
1286
 
1259
1287
  A backup has been taken using this backup definition:
1260
- ```
1288
+
1289
+ ``` code
1261
1290
  -R /
1262
1291
  -g home/user/Documents
1263
1292
  ```
1264
1293
 
1265
1294
  When restoring and using `/tmp` for --restore-dir, the restored files can be found in `/tmp/home/user/Documents`
1266
1295
 
1267
- ## a single file
1268
- ```
1296
+ ### a single file
1297
+
1298
+ ``` code
1269
1299
  . <the virtual env>/bin/activate
1270
1300
  dar-backup --restore <archive_name> --selection "-g path/to/file"
1271
1301
  deactivate
1302
+
1272
1303
  ```
1273
- ## a directory
1274
- ```
1304
+
1305
+ ### a directory
1306
+
1307
+ ``` bash
1275
1308
  . <the virtual env>/bin/activate
1276
1309
  dar-backup --restore <archive_name> --selection "-g path/to/directory"
1277
1310
  deactivate
1278
1311
  ```
1279
1312
 
1313
+ ### .NEF from a specific date
1280
1314
 
1281
- ## .NEF from a specific date
1282
- ```
1315
+ ``` bash
1283
1316
  . <the virtual env>/bin/activate
1284
1317
  dar-backup --restore <archive_name> --selection "-X '*.xmp' -I '*2024-06-16*' -g home/pj/tmp/LUT-play"
1285
1318
  deactivate
1286
1319
  ```
1287
1320
 
1321
+ ### restore test fails with exit code 4
1288
1322
 
1289
- # Points of interest
1290
-
1291
- ## .darrc sets -vd -vf (since v0.6.4)
1323
+ "dar" in newer versions emits a question about file ownership, which is "answered" with a "no" via the "-Q" option. That in turn leads to an error code 4.
1324
+
1325
+ Thus the dar option "--comparison-field=ignore-owner" has been placed in the supplied .darrc file (located in the virtual environment where dar-backup is installed).
1326
+
1327
+ This causes dar to restore without an error.
1328
+
1329
+ It is a good option when using dar as a non-privileged user.
1330
+
1331
+ ### restore test fails with exit code 5
1332
+
1333
+ If exit code 5 is emitted on the restore test, FSA (File System specific Attributes) could be the cause.
1334
+
1335
+ That (might) occur if you backup a file stored on one type of filesystem, and restore it on another type.
1336
+ My home directory is on a btrfs filesystem, while /tmp (for the restore test) is on zfs.
1337
+
1338
+ The restore test can result in an exit code 5, due to the different filesystems used. In order to avoid the errors, the "option "--fsa-scope none" can be used. That will restult in FSA's not being restored.
1339
+
1340
+ If you need to use this option, un-comment it in the .darrc file (located in the virtual environment where dar-backup is installed)
1341
+
1342
+ ## Par2
1343
+
1344
+ ### Par2 to verify/repair
1345
+
1346
+ You can run a par2 verification on an archive like this:
1347
+
1348
+ ```` bash
1349
+ for file in <archive>*.dar.par2; do
1350
+ par2 verify "$file"
1351
+ done
1352
+ ````
1353
+
1354
+ if there are problems with a slice, try to repair it like this:
1355
+
1356
+ ```` bash
1357
+ par2 repair <archive>.<slice number>.dar.par2
1358
+ ````
1359
+
1360
+ ### Par2 create redundancy files
1361
+
1362
+ If you have merged archives, you will need to create the .par2 redundency files manually.
1363
+ Here is an example
1364
+
1365
+ ```` bash
1366
+ for file in <some-archive>_FULL_yyyy-mm-dd.*; do
1367
+ par2 c -r5 -n1 "$file"
1368
+ done
1369
+ ````
1370
+
1371
+ where "c" is create, -r5 is 5% redundency and -n1 is 1 redundency file
1372
+
1373
+ ## Points of interest
1374
+
1375
+ ### Merge FULL with DIFF, creating new FULL
1376
+
1377
+ Over time, the DIFF archives become larger and larger. At some point one wishes to create a new FULL archive to do DIFF's on.
1378
+ One way to do that, is to let dar create a FULL archive from scratch, another is to merge a FULL archive with a DIFF, and from there do DIFF's until they once again gets too large for your taste.
1379
+
1380
+ I do backups of my homedir. Here it is shown how a FULL archive is merged with a DIFF, creating a new FULL archive.
1381
+
1382
+ ```` bash
1383
+ dar --merge pj-homedir_FULL_2021-09-12 -A pj-homedir_FULL_2021-06-06 -@pj-homedir_DIFF_2021-08-29 -s 12G
1384
+
1385
+ # test the new FULL archive
1386
+ dar -t pj-homedir_FULL_2021-09-12
1387
+
1388
+ # create Par2 redundancy files
1389
+ for file in pj-homedir_FULL_yyyy-mm-dd.*.dar; do
1390
+ par2 c -r5 -n1 "$file"
1391
+ done
1392
+
1393
+ ````
1394
+
1395
+ ### dar manager databases
1396
+
1397
+ `dar-backup` now saves archive catalogs in dar catalog databases.
1398
+
1399
+ This makes it easier to restore to a given date when having many FULL, DIFF and INCR archives.
1400
+
1401
+ ### Performance tip due to par2
1402
+
1403
+ This [dar benchmark page](https://dar.sourceforge.io/doc/benchmark.html) has an interesting note on the slice size.
1404
+
1405
+ Slice size should be smaller than available RAM, apparently a large performance hit can be avoided keeping the the par2 data in memory.
1406
+
1407
+ ### .darrc sets -vd -vf (since v0.6.4)
1292
1408
 
1293
1409
  These .darrc settings make `dar` print the current directory being processed (-vd) and some stats after (-vf)
1294
1410
  This is very useful in very long running jobs to get an indication that the backup is proceeding normally.
1295
1411
 
1296
- if --log-stdout is used the information would be picked up by systemd and logged by journald
1412
+ if --log-stdout is used the information would be picked up by systemd and logged by journald
1413
+
1414
+ The log file can get quite cluttered, if you want the clutter to be removed, run the `clean-log`script.
1415
+
1416
+ ## Reference
1417
+
1418
+ ### dar-backup.py
1419
+
1420
+ This script is responsible for managing the backup creation and validation process. It supports the following options:
1421
+
1422
+ ``` code
1423
+ --full-backup Perform a full backup.
1424
+ --differential-backup Perform a differential backup.
1425
+ --incremental-backup Perform an incremental backup.
1426
+ --backup-definition <name> Specify the backup definition file.
1427
+ --alternate-reference-archive <file> Use a different archive for DIFF/INCR backups.
1428
+ --config-file <path> Specify the path to the configuration file.
1429
+ --darrc <path> Specify an optional path to .darrc.
1430
+ --examples Show examples of using dar-backup.py.
1431
+ --list List available backups.
1432
+ --list-contents <archive> List the contents of a specified archive.
1433
+ --selection <params> Define file selection for listing/restoring.
1434
+ --restore <archive> Restore a specified archive.
1435
+ --restore-dir <path> Directory to restore files to.
1436
+ --verbose Enable verbose output.
1437
+ --log-level <level> Set log level (debug, trace, etc.).
1438
+ --log-stdout Also print log messages to stdout.
1439
+ --do-not-compare Do not compare restores to file system.
1440
+ --version Show version and license information.
1441
+ ```
1442
+
1443
+ ### manager.py
1444
+
1445
+ This script manages `dar` databases and catalogs. Available options include:
1446
+
1447
+ ``` code
1448
+ --create-db Create missing databases for all backup definitions.
1449
+ --alternate-archive-dir <path> Use this directory instead of BACKUP_DIR in the config file.
1450
+ --add-dir <path> Add all archive catalogs in this directory to databases.
1451
+ -d, --backup-def <name> Restrict to work only on this backup definition.
1452
+ --add-specific-archive <file> Add this archive to the catalog database.
1453
+ --remove-specific-archive <file> Remove this archive from the catalog database.
1454
+ -l, --list-catalogs List catalogs in databases for all backup definitions.
1455
+ --list-catalog-contents <num> List contents of a catalog by archive number.
1456
+ --list-archive-contents <file> List contents of an archive’s catalog.
1457
+ --find-file <file> Search catalogs for a specific file.
1458
+ --verbose Enable verbose output.
1459
+ --log-level <level> Set log level.
1460
+ ```
1461
+
1462
+ ### cleanup.py
1463
+
1464
+ This script cleans up old backups and manages storage. Supported options:
1465
+
1466
+ ``` code
1467
+ --prune-older-than <days> Remove backups older than the specified number of days.
1468
+ --keep-last <num> Retain only the last <num> backups.
1469
+ --dry-run Show what would be deleted without actually deleting.
1470
+ --backup-dir <path> Specify the backup directory.
1471
+ --verbose Enable verbose output.
1472
+ --help Show this help message and exit.
1473
+ ```
1474
+
1475
+ ### clean-log.py
1297
1476
 
1477
+ This script removes excessive logging output from `dar` logs, improving readability and efficiency. Available options:
1478
+
1479
+ ``` code
1480
+ -f, --file <path> Specify the log file(s) to be cleaned.
1481
+ -c, --config-file <path> Specify the configuration file (default: ~/.config/dar-backup/dar-backup.conf).
1482
+ --dry-run Show which lines would be removed without modifying the file.
1483
+ -v, --version Display version and licensing information.
1484
+ ```
@@ -0,0 +1,14 @@
1
+ dar_backup/.darrc,sha256=-aerqivZmOsW_XBCh9IfbYTUvw0GkzDSr3Vx4GcNB1g,2113
2
+ dar_backup/__about__.py,sha256=WAW-bfIrNx18qYeBN4lihEQibyosiKElDt_p7DfZELo,22
3
+ dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ dar_backup/clean_log.py,sha256=VXKA2BMyQmaC6R08Bq9a3wP3mczdFb_moy6HkL-mnF8,5176
5
+ dar_backup/cleanup.py,sha256=9yEdRR84XPtEvBGc2QfwGBQl2tdTPttjetHeiSc_TsM,11419
6
+ dar_backup/config_settings.py,sha256=CBMUhLOOZ-x7CRdS3vBDk4TYaGqC4N1Ot8IMH-qPaI0,3617
7
+ dar_backup/dar_backup.py,sha256=o4DvaVsqWHb1PxHIXfdr2qyqW6KRRGNcrR2_CRbPy1I,32942
8
+ dar_backup/manager.py,sha256=HDa8eYF89QFhlBRR4EWRzzmswOW00S_w8ToZ5SARO_o,21359
9
+ dar_backup/util.py,sha256=E-sEBQZY1hmdeVx5xNE22zKQ0BXDee1eI9F1-w7Fq1Q,15756
10
+ dar_backup-0.6.11.dist-info/METADATA,sha256=N7ICBJQEVDC01tza9lRAkmOXCIAILoDrg-mrHG926f0,71626
11
+ dar_backup-0.6.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ dar_backup-0.6.11.dist-info/entry_points.txt,sha256=p6c4uQLjlTIVP1Od2iorGefrVUH0IWZdFRMl63mNaRg,164
13
+ dar_backup-0.6.11.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
+ dar_backup-0.6.11.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- dar_backup/.darrc,sha256=-aerqivZmOsW_XBCh9IfbYTUvw0GkzDSr3Vx4GcNB1g,2113
2
- dar_backup/__about__.py,sha256=42fbYUSZonpHevrA9hTI2EzfgLNO9LBK95poG3HeDEQ,21
3
- dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- dar_backup/clean_log.py,sha256=VXKA2BMyQmaC6R08Bq9a3wP3mczdFb_moy6HkL-mnF8,5176
5
- dar_backup/cleanup.py,sha256=9yEdRR84XPtEvBGc2QfwGBQl2tdTPttjetHeiSc_TsM,11419
6
- dar_backup/config_settings.py,sha256=CBMUhLOOZ-x7CRdS3vBDk4TYaGqC4N1Ot8IMH-qPaI0,3617
7
- dar_backup/dar_backup.py,sha256=hDy7aXU-XiWOtW40Pxql441liNkSYKGU76eOwy8m7fU,32714
8
- dar_backup/manager.py,sha256=HDa8eYF89QFhlBRR4EWRzzmswOW00S_w8ToZ5SARO_o,21359
9
- dar_backup/util.py,sha256=SSSJYM9lQZfubhTUBlX1xDGWmCpYEF3ePARmlY544xM,11283
10
- dar_backup-0.6.9.dist-info/METADATA,sha256=mqAzHZp_-X9pw8w77c4FA7QwXnKikkSodJ-oJnaFT3M,64610
11
- dar_backup-0.6.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
- dar_backup-0.6.9.dist-info/entry_points.txt,sha256=p6c4uQLjlTIVP1Od2iorGefrVUH0IWZdFRMl63mNaRg,164
13
- dar_backup-0.6.9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
- dar_backup-0.6.9.dist-info/RECORD,,