Added check for magic values in media types

This commit is contained in:
Rob McMullen 2019-03-25 12:08:17 -07:00
parent 59ea74e182
commit 3897da6dc4
5 changed files with 52 additions and 29 deletions

View File

@ -92,9 +92,16 @@ class InvalidMediaSize(MediaError):
pass
class InvalidAtrHeader(MediaError):
class InvalidHeader(MediaError):
pass
class InvalidCartHeader(MediaError):
# Errors when trying to determine filesystem
class FilesystemError(AtrError):
pass
class IncompatibleMediaError(FilesystemError):
pass

View File

@ -31,6 +31,7 @@ class MediaType(Segment):
if self.header is not None:
self.check_header()
self.check_media_size()
self.check_magic()
#### initialization
@ -65,6 +66,16 @@ class MediaType(Segment):
"""
pass
def check_magic(self):
"""Subclasses should override this method if there is some "magic"
values that can identify (or rule out) this disk image class as a
canditate.
Subclasses should raise the appropriate MediaError if the data is
incompatible with this media type.
"""
pass
def find_filesystem(self):
fs = filesystem.guess_filesystem(self)
if fs:

View File

@ -104,7 +104,7 @@ def get_cart(cart_type):
try:
return known_cart_types[known_cart_type_map[cart_type]]
except KeyError:
raise errors.InvalidCartHeader("Unsupported cart type %d" % cart_type)
raise errors.InvalidHeader("Unsupported cart type %d" % cart_type)
class A8CartHeader:
@ -120,12 +120,12 @@ class A8CartHeader:
if len(data) == 16:
header = data.view(dtype=self.format)[0]
if header[0] != b'CART':
raise errors.InvalidCartHeader
raise errors.InvalidHeader
self.cart_type = int(header[1])
self.crc = int(header[2])
self.set_type(self.cart_type)
else:
raise errors.InvalidCartHeader
raise errors.InvalidHeader
def __str__(self):
return "%s Cartridge (atari800 type=%d size=%d, %d banks, crc=%d)" % (self.cart_name, self.cart_type, self.cart_size, self.bank_size, self.crc)
@ -150,7 +150,7 @@ class A8CartHeader:
def check_media(self, media):
media_size = len(media) - 16
if self.cart_size != media_size:
raise errors.InvalidCartHeader("Invalid cart size: {media_size}, expected {self.cart_size} for {self.cart_name}")
raise errors.InvalidHeader("Invalid cart size: {media_size}, expected {self.cart_size} for {self.cart_name}")
class Atari8bitCart(CartImage):

View File

@ -27,14 +27,14 @@ class AtrHeader(Segment):
if len(header) == 16:
values = header.view(dtype=self.format)[0]
if values[0] != 0x296:
raise errors.InvalidAtrHeader("no ATR header magic value")
raise errors.InvalidHeader("no ATR header magic value")
self.image_size = (int(values[3]) * 256 * 256 + int(values[1])) * 16
self.sector_size = int(values[2])
self.crc = int(values[4])
self.unused = int(values[5])
self.flags = int(values[6])
else:
raise errors.InvalidAtrHeader("incorrect AHC header size of %d" % len(bytes))
raise errors.InvalidHeader("incorrect AHC header size of %d" % len(bytes))
def encode(self, raw):
values = raw.view(dtype=self.format)[0]
@ -51,10 +51,10 @@ class AtrHeader(Segment):
def check_media(self, media):
if self.sector_size != media.sector_size:
raise errors.InvalidAtrHeader("ExpectedMismatch between sector sizes: header claims {self.sector_size}, expected {media.sector_size} for {media.pretty_name}")
raise errors.InvalidHeader("ExpectedMismatch between sector sizes: header claims {self.sector_size}, expected {media.sector_size} for {media.pretty_name}")
media_size = len(media) - 16
if self.image_size != media_size:
raise errors.InvalidAtrHeader("Invalid media size: header claims {self.image_size}, expected {media_size} for {media.pretty_name}")
raise errors.InvalidHeader("Invalid media size: header claims {self.image_size}, expected {media_size} for {media.pretty_name}")
class AtariSingleDensity(DiskImage):
@ -71,10 +71,10 @@ class AtariSingleDensity(DiskImage):
if len(header_data) == 16:
try:
header = AtrHeader(container)
except errors.InvalidAtrHeader:
except errors.InvalidHeader:
header = None
else:
raise errors.InvalidAtrHeader(f"file size {len(data)} small to be {self.pretty_name}")
raise errors.InvalidHeader(f"file size {len(data)} small to be {self.pretty_name}")
return header
@ -86,6 +86,14 @@ class AtariSingleDensityShortImage(AtariSingleDensity):
if size >= self.expected_size:
raise errors.InvalidMediaSize(f"{self.pretty_name} must be less than size {self.expected_size}")
def check_magic(self):
# Must have an ATR header for this to be a disk image
if self.header is None:
raise errors.InvalidHeader("Must have an ATR header for a non-standard image size")
flag = self[0:2].view(dtype='<u2')
if flag == 0xffff:
raise errors.InvalidHeader("Appears to be an executable")
class AtariEnhancedDensity(AtariSingleDensity):
pretty_name = "Atari ED (130K) Floppy Disk Image"

View File

@ -29,23 +29,20 @@ class TestMediaTypesInTestDataDir:
base_path = None
expected_mime = ""
def test_test_data_dir(self):
for pathname in sorted(glob.glob(os.path.join(os.path.dirname(__file__), "../test_data/", "*"))):
wrapped, ext = os.path.splitext(pathname)
if ext not in ext_to_valid_types:
# skip for now until recognize bare files
continue
print(f"checking {pathname}")
sample_data = np.fromfile(pathname, dtype=np.uint8)
container = guess_container(sample_data)
if container.compression_algorithm != "no compression":
_, ext = os.path.splitext(wrapped)
container.guess_media_type()
print(ext, ext_to_valid_types)
if ext in ext_to_valid_types:
assert container.media.__class__ in ext_to_valid_types[ext]
else:
assert container.media.__class__ == MediaType
@pytest.mark.parametrize("pathname", sorted(glob.glob(os.path.join(os.path.dirname(__file__), "../test_data/", "*"))))
def test_test_data_dir(self, pathname):
wrapped, ext = os.path.splitext(pathname)
print(f"checking {pathname}")
sample_data = np.fromfile(pathname, dtype=np.uint8)
container = guess_container(sample_data)
if container.compression_algorithm != "no compression":
_, ext = os.path.splitext(wrapped)
container.guess_media_type()
print(ext, ext_to_valid_types)
if ext in ext_to_valid_types:
assert container.media.__class__ in ext_to_valid_types[ext]
else:
assert container.media.__class__ == MediaType
if __name__ == "__main__":