@@ -1206,7 +1206,7 @@ def _check_hashes(self, file_object, trusted_hashes):
12061206
12071207
12081208
1209- def _hard_check_file_length (self , file_object , trusted_file_length ):
1209+ def _check_file_length (self , file_object , trusted_file_length ):
12101210 """
12111211 <Purpose>
12121212 Non-public method that ensures the length of 'file_object' is strictly
@@ -1233,8 +1233,10 @@ def _hard_check_file_length(self, file_object, trusted_file_length):
12331233 None.
12341234 """
12351235
1236- file_object .seek (0 )
1237- observed_length = len (file_object .read ())
1236+ # seek to the end of the file; that is offset 0 from the end of the file,
1237+ # represented by a whence value of 2
1238+ file_object .seek (0 , 2 )
1239+ observed_length = file_object .tell ()
12381240
12391241 # Return and log a message if the length 'file_object' is equal to
12401242 # 'trusted_file_length', otherwise raise an exception. A hard check
@@ -1252,53 +1254,6 @@ def _hard_check_file_length(self, file_object, trusted_file_length):
12521254
12531255
12541256
1255- def _soft_check_file_length (self , file_object , trusted_file_length ):
1256- """
1257- <Purpose>
1258- Non-public method that checks the trusted file length of a file object.
1259- The length of the file must be less than or equal to the expected
1260- length. This is a deliberately redundant implementation designed to
1261- complement tuf.download._check_downloaded_length().
1262-
1263- <Arguments>
1264- file_object:
1265- A file object.
1266-
1267- trusted_file_length:
1268- A non-negative integer that is the trusted length of the file.
1269-
1270- <Exceptions>
1271- tuf.exceptions.DownloadLengthMismatchError, if the lengths do
1272- not match.
1273-
1274- <Side Effects>
1275- Reads the contents of 'file_object' and logs a message if 'file_object'
1276- is less than or equal to the trusted length.
1277- Position within file_object is changed.
1278-
1279- <Returns>
1280- None.
1281- """
1282-
1283- # Read the entire contents of 'file_object', a
1284- file_object .seek (0 )
1285- observed_length = len (file_object .read ())
1286-
1287- # Return and log a message if 'file_object' is less than or equal to
1288- # 'trusted_file_length', otherwise raise an exception. A soft check
1289- # ensures that an upper bound restricts how large a file is downloaded.
1290- if observed_length > trusted_file_length :
1291- raise tuf .exceptions .DownloadLengthMismatchError (trusted_file_length ,
1292- observed_length )
1293-
1294- else :
1295- logger .debug ('Observed length (' + str (observed_length ) + \
1296- ') <= trusted length (' + str (trusted_file_length ) + ')' )
1297-
1298-
1299-
1300-
1301-
13021257 def _get_target_file (self , target_filepath , file_length , file_hashes ,
13031258 prefix_filename_with_hash ):
13041259 """
@@ -1340,24 +1295,39 @@ def _get_target_file(self, target_filepath, file_length, file_hashes,
13401295 A file object containing the target.
13411296 """
13421297
1343- # Define a callable function that is passed as an argument to _get_file()
1344- # and called. The 'verify_target_file' function ensures the file length
1345- # and hashes of 'target_filepath' are strictly equal to the trusted values.
1346- def verify_target_file (target_file_object ):
1347-
1348- # Every target file must have its length and hashes inspected.
1349- self ._hard_check_file_length (target_file_object , file_length )
1350- self ._check_hashes (target_file_object , file_hashes )
1351-
13521298 if self .consistent_snapshot and prefix_filename_with_hash :
13531299 # Note: values() does not return a list in Python 3. Use list()
13541300 # on values() for Python 2+3 compatibility.
13551301 target_digest = list (file_hashes .values ()).pop ()
13561302 dirname , basename = os .path .split (target_filepath )
13571303 target_filepath = os .path .join (dirname , target_digest + '.' + basename )
13581304
1359- return self ._get_file (target_filepath , verify_target_file ,
1360- 'target' , file_length , download_safely = True )
1305+ file_mirrors = tuf .mirrors .get_list_of_mirrors ('target' , target_filepath ,
1306+ self .mirrors )
1307+
1308+ # file_mirror (URL): error (Exception)
1309+ file_mirror_errors = {}
1310+ file_object = None
1311+
1312+ for file_mirror in file_mirrors :
1313+ try :
1314+ file_object = tuf .download .safe_download (file_mirror , file_length )
1315+
1316+ # Verify 'file_object' against the expected length and hashes.
1317+ self ._check_file_length (file_object , file_length )
1318+ self ._check_hashes (file_object , file_hashes )
1319+ # If the file verifies, we don't need to try more mirrors
1320+ return file_object
1321+
1322+ except Exception as exception :
1323+ # Remember the error from this mirror, and "reset" the target file.
1324+ logger .debug ('Update failed from ' + file_mirror + '.' )
1325+ file_mirror_errors [file_mirror ] = exception
1326+ file_object = None
1327+
1328+ logger .debug ('Failed to update ' + repr (target_filepath ) + ' from'
1329+ ' all mirrors: ' + repr (file_mirror_errors ))
1330+ raise tuf .exceptions .NoWorkingMirrorError (file_mirror_errors )
13611331
13621332
13631333
@@ -1641,96 +1611,6 @@ def _get_metadata_file(self, metadata_role, remote_filename,
16411611
16421612
16431613
1644- def _get_file (self , filepath , verify_file_function , file_type , file_length ,
1645- download_safely = True ):
1646- """
1647- <Purpose>
1648- Non-public method that tries downloading, up to a certain length, a
1649- metadata or target file from a list of known mirrors. As soon as the first
1650- valid copy of the file is found, the rest of the mirrors will be skipped.
1651-
1652- <Arguments>
1653- filepath:
1654- The relative metadata or target filepath.
1655-
1656- verify_file_function:
1657- A callable function that expects a file object and raises an exception
1658- if the file is invalid.
1659- Target files and uncompressed versions of metadata may be verified with
1660- 'verify_file_function'.
1661-
1662- file_type:
1663- Type of data needed for download, must correspond to one of the strings
1664- in the list ['meta', 'target']. 'meta' for metadata file type or
1665- 'target' for target file type. It should correspond to the
1666- 'securesystemslib.formats.NAME_SCHEMA' format.
1667-
1668- file_length:
1669- The expected length, or upper bound, of the target or metadata file to
1670- be downloaded.
1671-
1672- download_safely:
1673- A boolean switch to toggle safe or unsafe download of the file.
1674-
1675- <Exceptions>
1676- tuf.exceptions.NoWorkingMirrorError:
1677- The metadata could not be fetched. This is raised only when all known
1678- mirrors failed to provide a valid copy of the desired metadata file.
1679-
1680- <Side Effects>
1681- The file is downloaded from all known repository mirrors in the worst
1682- case. If a valid copy of the file is found, it is stored in a temporary
1683- file and returned.
1684-
1685- <Returns>
1686- A file object containing the metadata or target.
1687- """
1688-
1689- file_mirrors = tuf .mirrors .get_list_of_mirrors (file_type , filepath ,
1690- self .mirrors )
1691-
1692- # file_mirror (URL): error (Exception)
1693- file_mirror_errors = {}
1694- file_object = None
1695-
1696- for file_mirror in file_mirrors :
1697- try :
1698- # TODO: Instead of the more fragile 'download_safely' switch, unroll
1699- # the function into two separate ones: one for "safe" download, and the
1700- # other one for "unsafe" download? This should induce safer and more
1701- # readable code.
1702- if download_safely :
1703- file_object = tuf .download .safe_download (file_mirror , file_length )
1704-
1705- else :
1706- file_object = tuf .download .unsafe_download (file_mirror , file_length )
1707-
1708- # Verify 'file_object' according to the callable function.
1709- # 'file_object' is also verified if decompressed above (i.e., the
1710- # uncompressed version).
1711- verify_file_function (file_object )
1712-
1713- except Exception as exception :
1714- # Remember the error from this mirror, and "reset" the target file.
1715- logger .debug ('Update failed from ' + file_mirror + '.' )
1716- file_mirror_errors [file_mirror ] = exception
1717- file_object = None
1718-
1719- else :
1720- break
1721-
1722- if file_object :
1723- return file_object
1724-
1725- else :
1726- logger .debug ('Failed to update ' + repr (filepath ) + ' from'
1727- ' all mirrors: ' + repr (file_mirror_errors ))
1728- raise tuf .exceptions .NoWorkingMirrorError (file_mirror_errors )
1729-
1730-
1731-
1732-
1733-
17341614 def _update_metadata (self , metadata_role , upperbound_filelength , version = None ):
17351615 """
17361616 <Purpose>
0 commit comments