Index

s3-bsync / bfb1fb4

Bidirectional syncing tool to sync local filesystem directories with S3 buckets. (Incomplete)

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
1216 Jun 2022 14:26bfb1fb4Update module handling and settingsJosh Stockin12926G

Blob @ s3-bsync / src / syncfile.py

application/x-python3097 bytesdownload raw
1# s3-bsync Copyright (c) 2022 Joshua Stockin
2# <https://joshstock.in>
3# <https://git.joshstock.in/s3-bsync>
4#
5# This software is licensed and distributed under the terms of the MIT License.
6# See the MIT License in the LICENSE file of this project's root folder.
7#
8# This comment block and its contents, including this disclaimer, MUST be
9# preserved in all copies or distributions of this software's source.
10
11import os
12import logging
13
14from .classes import *
15
16logger = logging.getLogger(__name__)
17
18__all__ = ["syncfile"]
19
20
21CONTROL_BYTES = {
22 "SIGNATURE": b"\x9D\x9F\x53\x33",
23 "BUCKET_BEGIN": b"\x90",
24 "BUCKET_END": b"\x91",
25 "DIRECTORY_BEGIN": b"\x92",
26 "DIRECTORY_END": b"\x93",
27 "OBJECT_BEGIN": b"\x94",
28 "OBJECT_END": b"\x95",
29 "ETAG_MD5": b"\x96",
30 "ETAG_OTHER": b"\x97",
31 "METADATA_BEGIN": b"\x9A",
32 "METADATA_END": b"\x9B",
33}
34
35CURRENT_VERSION = 1
36ENDIANNESS = "little"
37
38
39class CorruptSyncfileException(Exception):
40 """Exception passed by syncfile class when experiencing errors deserializing a supplied s3sync file."""
41
42
43class syncfile:
44 file_path = None
45 file = None
46 file_version = 0
47 last_synced_time = 0
48 tracked_buckets = {}
49
50 def __init__(self, state_file: str):
51 self.file_path = state_file
52 self.file = open(state_file, "wb+")
53 logger.debug(f"Opened s3sync state file at {state_file}")
54
55 def file_exists(self):
56 if os.path.exists(self.file_path) and not os.path.isdir(self.file_path):
57 return True
58 return False
59
60 def deserialize(self):
61 f = self.file
62 logger.debug(f"Deserializing file {f}")
63 f.seek(0)
64
65 def get_string():
66 return "".join(iter(lambda: f.read(1), "\x00"))
67
68 b = f.read(4)
69 if b is not CONTROL_BYTES["SIGNATURE"]:
70 logger.error(
71 "File signature does not match expected s3state file signature (not an s3sync file format or file corrupted)"
72 )
73 exit(1)
74
75 self.file_version = int(f.read(1))
76 if self.file_version is 0 or self.file_version >= 1:
77 logger.error(
78 f"File version outside expected range (1..{CURRENT_VERSION}) (corrupt file)"
79 )
80 exit(1)
81
82 b = f.read(1)
83 if b is not CONTROL_BYTES["METADATA_BEGIN"]:
84 logger.error("Expected metadata block begin byte not found (corrupt file)")
85 exit(1)
86 if self.file_version <= 1:
87 self.last_synced_time = int.from_bytes(b.read(8), byteorder=ENDIANNESS)
88 logger.debug(f"Last synced time reported as {self.last_synced_time}")
89
90 b = f.read(1)
91 if b is not CONTROL_BYTES["METADATA_END"]:
92 logger.error("Expected metadata block end byte not found (corrupt file)")
93 exit(1)
94
95 while b := f.read(1):
96 if b is not CONTROL_BYTES["BUCKET_BEGIN"]:
97 logger.error(b"Unexpected control byte {b} detected (corrupt file)")
98 exit(1)
99 bucket_name = get_string()
100 bucket = classes.bucket(bucket_name)
101