Skip to content

Commit 9efb1c2

Browse files
🐛 [-bug] Properly using working_dir across all restoration activities (#15)
2 parents 734631b + 369466d commit 9efb1c2

File tree

1 file changed

+55
-33
lines changed

1 file changed

+55
-33
lines changed

src/FileBackup/Offsite.py

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ def Restore(
555555
)
556556
return
557557

558-
with _YieldTempDirectory("staging content") as staging_directory:
558+
with _YieldTempDirectory(working_dir / "staging", "staging content") as staging_directory:
559559
# ----------------------------------------------------------------------
560560
@dataclass(frozen=True)
561561
class Instruction:
@@ -709,26 +709,38 @@ def PrepareTask(
709709
def ExecuteTask(
710710
status: ExecuteTasks.Status,
711711
) -> Path:
712-
destination_dir = working_dir / directory
713-
714-
if destination_dir.is_dir():
712+
# This function will create the following directory structure:
713+
#
714+
# <working_dir>
715+
# └── <directory>
716+
# └── transferred (temporary)
717+
# └── decompressed (temporary)
718+
# └── final
719+
720+
assert working_dir is not None
721+
this_working_dir = working_dir / directory
722+
final_dir = this_working_dir / "final"
723+
724+
if final_dir.is_dir():
715725
# The destination already exists, no need to process it further
716-
return destination_dir
726+
return final_dir
717727

718-
with _YieldRestoredArchive(
728+
with _YieldTransferredArchive(
719729
data_store, # type: ignore
720730
directory,
731+
this_working_dir / "transferred",
721732
lambda bytes_transferred: cast(
722733
None,
723734
status.OnProgress(
724735
ProcessDirectoryState.Transferring.value + 1,
725736
bytes_transferred,
726737
),
727738
),
728-
) as (archive_directory, archive_directory_is_temporary):
729-
with _YieldRestoredFiles(
739+
) as (transferred_dir, transferred_dir_is_temporary):
740+
with _YieldDecompressedFiles(
741+
transferred_dir,
742+
this_working_dir / "decompressed",
730743
directory,
731-
archive_directory,
732744
encryption_password,
733745
lambda message: cast(
734746
None,
@@ -737,11 +749,11 @@ def ExecuteTask(
737749
message,
738750
),
739751
),
740-
) as (contents_dir, contents_dir_is_temporary):
752+
) as (decompressed_dir, decompressed_dir_is_temporary):
741753
# Validate the contents
742-
_VerifyRestoredFiles(
754+
_VerifyFiles(
743755
directory,
744-
contents_dir,
756+
decompressed_dir,
745757
lambda message: cast(
746758
None,
747759
status.OnProgress(
@@ -755,7 +767,7 @@ def ExecuteTask(
755767
# directory structure and doesn't do anything to account for
756768
# nested dirs. This assumption matches the current archive
757769
# format.
758-
if archive_directory_is_temporary or contents_dir_is_temporary:
770+
if transferred_dir_is_temporary or decompressed_dir_is_temporary:
759771
func = cast(Callable[[Path, Path], None], shutil.move)
760772
else:
761773
# ----------------------------------------------------------------------
@@ -770,14 +782,14 @@ def CreateSymLink(
770782

771783
func = CreateSymLink
772784

773-
temp_dest_dir = destination_dir.parent / (destination_dir.name + "__temp__")
785+
temp_dest_dir = final_dir.parent / (final_dir.name + "__temp__")
774786

775787
shutil.rmtree(temp_dest_dir, ignore_errors=True)
776788
temp_dest_dir.mkdir(parents=True)
777789

778790
items = [
779791
item
780-
for item in contents_dir.iterdir()
792+
for item in decompressed_dir.iterdir()
781793
if item.name != INDEX_HASH_FILENAME
782794
]
783795

@@ -789,9 +801,9 @@ def CreateSymLink(
789801

790802
func(item, temp_dest_dir)
791803

792-
shutil.move(temp_dest_dir, destination_dir)
804+
shutil.move(temp_dest_dir, final_dir)
793805

794-
return destination_dir
806+
return final_dir
795807

796808
# ----------------------------------------------------------------------
797809

@@ -960,7 +972,8 @@ def PathToFilename(
960972
with dm.Nested("\nProcessing instructions...") as all_instructions_dm:
961973
all_instructions_dm.WriteLine("")
962974

963-
temp_directory = PathEx.CreateTempDirectory()
975+
temp_directory = working_dir / "instructions"
976+
temp_directory.mkdir(parents=True, exist_ok=True)
964977

965978
with ExitStack(lambda: shutil.rmtree(temp_directory)):
966979
commit_actions: list[Callable[[], None]] = []
@@ -1287,9 +1300,9 @@ def CommitContext(
12871300
# ----------------------------------------------------------------------
12881301
@contextmanager
12891302
def _YieldTempDirectory(
1303+
temp_directory: Path,
12901304
desc: str,
12911305
) -> Iterator[Path]:
1292-
temp_directory = PathEx.CreateTempDirectory()
12931306
should_delete = True
12941307

12951308
try:
@@ -1299,7 +1312,8 @@ def _YieldTempDirectory(
12991312
raise
13001313
finally:
13011314
if should_delete:
1302-
shutil.rmtree(temp_directory)
1315+
if temp_directory.is_dir():
1316+
shutil.rmtree(temp_directory)
13031317
else:
13041318
sys.stderr.write(
13051319
f"**** The temporary directory '{temp_directory}' was preserved due to errors while {desc}.\n",
@@ -1308,16 +1322,19 @@ def _YieldTempDirectory(
13081322

13091323
# ----------------------------------------------------------------------
13101324
@contextmanager
1311-
def _YieldRestoredArchive(
1325+
def _YieldTransferredArchive(
13121326
data_store: FileBasedDataStore,
13131327
directory: str,
1328+
working_dir: Path,
13141329
status_func: Callable[[str], None],
13151330
) -> Iterator[
13161331
tuple[
13171332
Path,
13181333
bool, # is temporary directory
13191334
],
13201335
]:
1336+
"""Transfer content from the data store to the local filesystem."""
1337+
13211338
if data_store.is_local_filesystem:
13221339
working_dir = data_store.GetWorkingDir() / directory
13231340
assert working_dir.is_dir(), working_dir
@@ -1327,7 +1344,7 @@ def _YieldRestoredArchive(
13271344

13281345
status_func("Calculating files to transfer...")
13291346

1330-
with _YieldTempDirectory("transferring archive files") as temp_directory:
1347+
with _YieldTempDirectory(working_dir, "transferring archive files") as working_dir:
13311348
# Map the remote filenames to local filenames
13321349
filename_map: dict[Path, Path] = {}
13331350

@@ -1339,7 +1356,7 @@ def _YieldRestoredArchive(
13391356
relative_root = root.relative_to(data_store_dir)
13401357

13411358
for filename in filenames:
1342-
filename_map[root / filename] = temp_directory / relative_root / filename
1359+
filename_map[root / filename] = working_dir / relative_root / filename
13431360

13441361
if not filename_map:
13451362
raise Exception(f"The directory '{directory}' does not contain any files.")
@@ -1359,14 +1376,15 @@ def _YieldRestoredArchive(
13591376
),
13601377
)
13611378

1362-
yield temp_directory, True
1379+
yield working_dir, True
13631380

13641381

13651382
# ----------------------------------------------------------------------
13661383
@contextmanager
1367-
def _YieldRestoredFiles(
1384+
def _YieldDecompressedFiles(
1385+
transferred_dir: Path,
1386+
decompressed_dir: Path,
13681387
directory_name: str,
1369-
archive_dir: Path,
13701388
encryption_password: str | None,
13711389
status_func: Callable[[str], None],
13721390
) -> Iterator[
@@ -1375,8 +1393,10 @@ def _YieldRestoredFiles(
13751393
bool, # is temporary directory
13761394
],
13771395
]:
1378-
if (archive_dir / INDEX_FILENAME).is_file():
1379-
yield archive_dir, False
1396+
"""Decompress the archive if necessary"""
1397+
1398+
if (transferred_dir / INDEX_FILENAME).is_file():
1399+
yield transferred_dir, False
13801400
return
13811401

13821402
# By default, 7zip will prompt for a password with archives that were created
@@ -1394,7 +1414,7 @@ def _YieldRestoredFiles(
13941414
# Validate
13951415
status_func("Validating archive...")
13961416

1397-
archive_filename = archive_dir / (ARCHIVE_FILENAME + ".001")
1417+
archive_filename = transferred_dir / (ARCHIVE_FILENAME + ".001")
13981418

13991419
if not archive_filename.is_file():
14001420
raise Exception(f"The archive file '{archive_filename.name}' was not found.")
@@ -1424,10 +1444,12 @@ def _YieldRestoredFiles(
14241444
# Extract
14251445
status_func("Extracting archive...")
14261446

1427-
with _YieldTempDirectory("extracting the archive") as temp_directory:
1447+
with _YieldTempDirectory(decompressed_dir, "extracting the archive") as decompressed_dir:
1448+
decompressed_dir.mkdir(parents=True, exist_ok=True)
1449+
14281450
result = SubprocessEx.Run(
14291451
f'{_GetZipBinary()} x "{archive_filename}" "-p{password}"',
1430-
cwd=temp_directory,
1452+
cwd=decompressed_dir,
14311453
)
14321454

14331455
if result.returncode != 0:
@@ -1450,11 +1472,11 @@ def _YieldRestoredFiles(
14501472
),
14511473
)
14521474

1453-
yield temp_directory, True
1475+
yield decompressed_dir, True
14541476

14551477

14561478
# ----------------------------------------------------------------------
1457-
def _VerifyRestoredFiles(
1479+
def _VerifyFiles(
14581480
directory_name: str,
14591481
contents_dir: Path,
14601482
status_func: Callable[[str], None],

0 commit comments

Comments
 (0)