Skip to content

Commit eeb48f6

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 048b43c commit eeb48f6

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
@@ -349,50 +349,53 @@ def _persist_file(self, filename: str, data: bytes) -> None:
349349
def _load_root(self) -> None:
350350
"""Load root metadata.
351351
352-
Sequentially load and persist every newer root metadata
353-
version available, either locally or on the remote.
352+
Sequentially load newer root metadata versions. First try to load from
353+
local cache and if that does not work, from the remote repository.
354+
355+
If metadata is loaded from remote repository, store it in local cache.
354356
"""
355357

356358
# Update the root role
357359
lower_bound = self._trusted_set.root.version + 1
358360
upper_bound = lower_bound + self.config.max_root_rotations
359361

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

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

0 commit comments

Comments
 (0)