Skip to content

Commit 8519bb4

Browse files
committed
ngclient: Make sure non-versioned link in cache is up-to-date
Even if last root version from remote is not accepted (leading to an exception in load_root()) we should update the symlink "root.json" in local cache to point to last good version. Signed-off-by: Jussi Kukkonen <[email protected]>
1 parent 4aa09ff commit 8519bb4

File tree

1 file changed

+41
-38
lines changed

1 file changed

+41
-38
lines changed

tuf/ngclient/updater.py

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -343,50 +343,53 @@ def _persist_file(self, filename: str, data: bytes) -> None:
343343
def _load_root(self) -> None:
344344
"""Load root metadata.
345345
346-
Sequentially load and persist every newer root metadata
347-
version available, either locally or on the remote.
346+
Sequentially load newer root metadata versions. First try to load from
347+
local cache and if that does not work, from the remote repository.
348+
349+
If metadata is loaded from remote repository, store it in local cache.
348350
"""
349351

350352
# Update the root role
351353
lower_bound = self._trusted_set.root.version + 1
352354
upper_bound = lower_bound + self.config.max_root_rotations
353355

354-
for next_version in range(lower_bound, upper_bound):
355-
# look for next_version in local cache
356-
try:
357-
root_path = os.path.join(
358-
self._dir, "root_history", f"{next_version}.root.json"
359-
)
360-
with open(root_path, "rb") as f:
361-
self._trusted_set.update_root(f.read())
362-
continue
363-
except (OSError, exceptions.RepositoryError) as e:
364-
# this root did not exist locally or is invalid
365-
logger.debug("Local root is not valid: %s", e)
366-
367-
# next_version was not found locally, try remote
368-
try:
369-
data = self._download_metadata(
370-
Root.type,
371-
self.config.root_max_length,
372-
next_version,
373-
)
374-
self._trusted_set.update_root(data)
375-
self._persist_root(next_version, data)
376-
377-
except exceptions.DownloadHTTPError as exception:
378-
if exception.status_code not in {403, 404}:
379-
raise
380-
# 404/403 means current root is newest available
381-
break
382-
383-
# Make sure there's a non-versioned root.json
384-
linkname = os.path.join(self._dir, "root.json")
385-
version = self._trusted_set.root.version
386-
current = os.path.join("root_history", f"{version}.root.json")
387-
with contextlib.suppress(FileNotFoundError):
388-
os.remove(linkname)
389-
os.symlink(current, linkname)
356+
try:
357+
for next_version in range(lower_bound, upper_bound):
358+
# look for next_version in local cache
359+
try:
360+
root_path = os.path.join(
361+
self._dir, "root_history", f"{next_version}.root.json"
362+
)
363+
with open(root_path, "rb") as f:
364+
self._trusted_set.update_root(f.read())
365+
continue
366+
except (OSError, exceptions.RepositoryError) as e:
367+
# this root did not exist locally or is invalid
368+
logger.debug("Local root is not valid: %s", e)
369+
370+
# next_version was not found locally, try remote
371+
try:
372+
data = self._download_metadata(
373+
Root.type,
374+
self.config.root_max_length,
375+
next_version,
376+
)
377+
self._trusted_set.update_root(data)
378+
self._persist_root(next_version, data)
379+
380+
except exceptions.DownloadHTTPError as exception:
381+
if exception.status_code not in {403, 404}:
382+
raise
383+
# 404/403 means current root is newest available
384+
break
385+
finally:
386+
# Make sure the non-versioned root.json links to current version
387+
linkname = os.path.join(self._dir, "root.json")
388+
version = self._trusted_set.root.version
389+
current = os.path.join("root_history", f"{version}.root.json")
390+
with contextlib.suppress(FileNotFoundError):
391+
os.remove(linkname)
392+
os.symlink(current, linkname)
390393

391394
def _load_timestamp(self) -> None:
392395
"""Load local and remote timestamp metadata."""

0 commit comments

Comments
 (0)