@@ -1555,17 +1555,33 @@ def _update_metadata_if_changed(self, metadata_role,
1555
1555
# Ensure the referenced metadata has been loaded. The 'root' role may be
1556
1556
# updated without having 'snapshot' available.
1557
1557
if referenced_metadata not in self .metadata ['current' ]:
1558
- message = 'Cannot update ' + repr (metadata_role ) + ' because ' \
1559
- + referenced_metadata + ' is missing.'
1560
- raise tuf .RepositoryError (message )
1558
+ raise tuf .RepositoryError ('Cannot update ' + repr (metadata_role ) +
1559
+ ' because ' + referenced_metadata + ' is missing.' )
1561
1560
1562
- # The referenced metadata has been loaded. Extract the new
1563
- # versioninfo for 'metadata_role' from it.
1561
+ # The referenced metadata has been loaded. Extract the new versioninfo for
1562
+ # 'metadata_role' from it.
1564
1563
else :
1565
- message = repr (metadata_role ) + ' referenced in ' + \
1566
- repr (referenced_metadata )+ '. ' + repr (metadata_role )+ ' may be updated.'
1567
- logger .debug (message )
1564
+ logger .debug (repr (metadata_role ) + ' referenced in ' +
1565
+ repr (referenced_metadata )+ '. ' + repr (metadata_role ) +
1566
+ ' may be updated.' )
1567
+
1568
+ # Extract the versioninfo of the uncompressed version of 'metadata_role'.
1569
+ expected_versioninfo = self .metadata ['current' ][referenced_metadata ] \
1570
+ ['meta' ] \
1571
+ [uncompressed_metadata_filename ]
1572
+
1573
+ # Simply return if the metadata for 'metadata_role' has not been updated,
1574
+ # according to the uncompressed metadata provided by the referenced
1575
+ # metadata. The metadata is considered updated if its version number is
1576
+ # strictly greater than its currently trusted version number.
1577
+ if not self ._versioninfo_has_been_updated (uncompressed_metadata_filename ,
1578
+ expected_versioninfo ):
1579
+ logger .info (repr (uncompressed_metadata_filename ) + ' up-to-date.' )
1580
+
1581
+ return
1568
1582
1583
+ logger .debug ('Metadata ' + repr (uncompressed_metadata_filename ) + ' has changed.' )
1584
+
1569
1585
# There might be a compressed version of 'snapshot.json' or Targets
1570
1586
# metadata available for download. Check the 'meta' field of
1571
1587
# 'referenced_metadata' to see if it is listed when 'metadata_role'
@@ -1581,38 +1597,22 @@ def _update_metadata_if_changed(self, metadata_role,
1581
1597
# decompressing a file that may be invalid or partially intact.
1582
1598
compression = None
1583
1599
1584
- # Extract the versioninfo of the uncompressed version of 'metadata_role'.
1585
- expected_versioninfo = self .metadata ['current' ][referenced_metadata ] \
1586
- ['meta' ] \
1587
- [uncompressed_metadata_filename ]
1588
-
1589
1600
# Check for the availability of compressed versions of 'snapshot.json',
1590
1601
# 'targets.json', and delegated Targets (that also start with 'targets').
1591
1602
# For 'targets.json' and delegated metadata, 'referenced_metata'
1592
1603
# should always be 'snapshot'. 'snapshot.json' specifies all roles
1593
1604
# provided by a repository, including their version numbers.
1594
1605
if metadata_role == 'snapshot' or metadata_role .startswith ('targets' ):
1595
- gzip_metadata_filename = uncompressed_metadata_filename + '.gz'
1596
1606
if 'gzip' in self .metadata ['current' ]['root' ]['compression_algorithms' ]:
1597
1607
compression = 'gzip'
1598
-
1599
- logger .debug ('Compressed version of ' + \
1600
- repr (uncompressed_metadata_filename ) + ' is available at ' + \
1601
- repr (gzip_metadata_filename ) + '.' )
1602
- else :
1603
- logger .debug ('Compressed version of ' + \
1604
- repr (uncompressed_metadata_filename ) + ' not available.' )
1605
-
1606
- # Simply return if the file has not changed, according to the metadata
1607
- # about the uncompressed file provided by the referenced metadata.
1608
- if not self ._versioninfo_has_changed (uncompressed_metadata_filename ,
1609
- expected_versioninfo ):
1610
- logger .info (repr (uncompressed_metadata_filename ) + ' up-to-date.' )
1608
+ gzip_metadata_filename = uncompressed_metadata_filename + '.gz'
1609
+ logger .debug ('Compressed version of ' +
1610
+ repr (uncompressed_metadata_filename ) + ' is available at ' +
1611
+ repr (gzip_metadata_filename ) + '.' )
1611
1612
1612
- return
1613
-
1614
- logger .debug ('Metadata ' + repr (uncompressed_metadata_filename ) + \
1615
- ' has changed.' )
1613
+ else :
1614
+ logger .debug ('Compressed version of ' +
1615
+ repr (uncompressed_metadata_filename ) + ' not available.' )
1616
1616
1617
1617
# The file lengths of metadata are unknown, only their version numbers are
1618
1618
# known. Set an upper limit for the length of the downloaded file for each
@@ -1659,14 +1659,15 @@ def _update_metadata_if_changed(self, metadata_role,
1659
1659
1660
1660
1661
1661
1662
- def _versioninfo_has_changed (self , metadata_filename , new_versioninfo ):
1662
+ def _versioninfo_has_been_updated (self , metadata_filename , new_versioninfo ):
1663
1663
"""
1664
1664
<Purpose>
1665
1665
Non-public method that determines whether the current versioninfo of
1666
- 'metadata_filename' differs from 'new_versioninfo'. The 'new_versioninfo'
1667
- argument should be extracted from the latest copy of the metadata that
1668
- references 'metadata_filename'. Example: 'root.json' would be referenced
1669
- by 'snapshot.json'.
1666
+ 'metadata_filename' is less than 'new_versioninfo' (i.e., the version
1667
+ number has been incremented). The 'new_versioninfo' argument should be
1668
+ extracted from the latest copy of the metadata that references
1669
+ 'metadata_filename'. Example: 'root.json' would be referenced by
1670
+ 'snapshot.json'.
1670
1671
1671
1672
'new_versioninfo' should only be 'None' if this is for updating
1672
1673
'root.json' without having 'snapshot.json' available.
@@ -1689,11 +1690,11 @@ def _versioninfo_has_changed(self, metadata_filename, new_versioninfo):
1689
1690
None.
1690
1691
1691
1692
<Side Effects>
1692
- If there is no versioninfo currently loaded for 'metada_filename',
1693
- try to load it.
1693
+ If there is no versioninfo currently loaded for 'metadata_filename', try
1694
+ to load it.
1694
1695
1695
1696
<Returns>
1696
- Boolean. True if the versioninfo has changed, false otherwise.
1697
+ Boolean. True if the versioninfo has changed, False otherwise.
1697
1698
"""
1698
1699
1699
1700
# If there is no versioninfo currently stored for 'metadata_filename',
@@ -1763,9 +1764,10 @@ def _update_versioninfo(self, metadata_filename):
1763
1764
self .metadata ['current' ]['timestamp' ]['version' ]
1764
1765
1765
1766
# When updating snapshot.json, the client either (1) has a copy of
1766
- # snapshot.json, or (2) in the process of obtaining it by first downloading
1767
- # timestamp.json. Note: Clients may have only root.json and perform a
1768
- # refresh of top-level metadata to obtain the remaining roles.
1767
+ # snapshot.json, or (2) is in the process of obtaining it by first
1768
+ # downloading timestamp.json. Note: Clients are allowed to have only
1769
+ # root.json initially, and perform a refresh of top-level metadata to
1770
+ # obtain the remaining roles.
1769
1771
elif metadata_filename == 'snapshot.json' :
1770
1772
1771
1773
# Verify the version number of the currently trusted snapshot.json in
@@ -2791,8 +2793,9 @@ def download_target(self, target, destination_directory):
2791
2793
<Purpose>
2792
2794
Download 'target' and verify it is trusted.
2793
2795
2794
- This will only store the file at 'destination_directory' if the downloaded
2795
- file matches the description of the file in the trusted metadata.
2796
+ This will only store the file at 'destination_directory' if the
2797
+ downloaded file matches the description of the file in the trusted
2798
+ metadata.
2796
2799
2797
2800
<Arguments>
2798
2801
target:
@@ -2809,6 +2812,10 @@ def download_target(self, target, destination_directory):
2809
2812
tuf.NoWorkingMirrorError:
2810
2813
If a target could not be downloaded from any of the mirrors.
2811
2814
2815
+ Although expected to be rare, there might be OSError exceptions (except
2816
+ errno.EEXIST) raised when creating the destination directory (if it
2817
+ doesn't exist).
2818
+
2812
2819
<Side Effects>
2813
2820
A target file is saved to the local system.
2814
2821
@@ -2834,15 +2841,20 @@ def download_target(self, target, destination_directory):
2834
2841
target_file_object = self ._get_target_file (target_filepath , trusted_length ,
2835
2842
trusted_hashes )
2836
2843
2837
- # We acquired a target file object from a mirror. Move the file into
2838
- # place (i.e., locally to 'destination_directory'). Note: join() discards
2839
- # 'destination_directory' if 'target_path' contains a leading path separator
2840
- # (i.e., is treated as an absolute path).
2844
+ # We acquired a target file object from a mirror. Move the file into place
2845
+ # (i.e., locally to 'destination_directory'). Note: join() discards
2846
+ # 'destination_directory' if 'target_path' contains a leading path
2847
+ # separator (i.e., is treated as an absolute path).
2841
2848
destination = os .path .join (destination_directory ,
2842
2849
target_filepath .lstrip (os .sep ))
2843
2850
destination = os .path .abspath (destination )
2844
2851
target_dirpath = os .path .dirname (destination )
2845
-
2852
+
2853
+ # When attempting to create the leaf directory of 'target_dirpath', ignore
2854
+ # any exceptions raised if the root directory already exists. All other
2855
+ # exceptions potentially thrown by os.makedirs() are re-raised.
2856
+ # Note: os.makedirs can raise OSError if the leaf directory already exists
2857
+ # or cannot be created.
2846
2858
try :
2847
2859
os .makedirs (target_dirpath )
2848
2860
0 commit comments