Module refinery.lib.iso
Library for parsing ISO 9660 and UDF disk images.
Expand source code Browse git
"""
Library for parsing ISO 9660 and UDF disk images.
"""
from __future__ import annotations
import datetime
import enum
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from refinery.lib.iso.iso9660 import ISORef
from refinery.lib.iso.udf import UDFRef
class FileSystemType(enum.Enum):
AUTO = 'auto'
ISO = 'iso'
JOLIET = 'joliet'
RR = 'rr'
UDF = 'udf'
class ISOFile:
"""
Represents a single file entry within an ISO image.
"""
__slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key')
def __init__(
self,
path: str,
size: int,
date: datetime.datetime | None,
is_dir: bool,
extents: list[tuple[int, int]],
key: tuple[FileSystemType, ISORef | UDFRef],
inline: bytes | None = None,
):
self.path = path
self.size = size
self.date = date
self.is_dir = is_dir
self.extents = extents
self._inline = inline
self.key = key
class ISOArchive:
"""
Unified ISO/UDF archive reader. Tries UDF first, falls back to ISO 9660.
"""
def __init__(self, data):
from refinery.lib.iso.udf import ANCHOR_SECTOR, UDFArchive, _verify_tag
self._data = data
self._iso = None
self._udf = None
self._type = FileSystemType.ISO
udf_found = False
dv = memoryview(self._data)
for ss in (2048, 512, 4096):
anchor_pos = ANCHOR_SECTOR * ss
if anchor_pos + 16 <= len(dv):
tag_id = _verify_tag(dv, anchor_pos)
if tag_id == 2:
udf_found = True
break
if udf_found:
udf = UDFArchive()
try:
udf.open(self._data)
except Exception:
udf = None
if udf is not None and udf.refs:
self._udf = udf
self._type = FileSystemType.UDF
return
from refinery.lib.iso.iso9660 import ISO9660Archive
iso = ISO9660Archive()
iso.open(self._data)
self._iso = iso
self._type = iso.filesystem_type
@property
def filesystem_type(self) -> str:
return self._type.value
def select_filesystem(self, fs: FileSystemType) -> None:
if fs is FileSystemType.UDF:
if self._udf is not None:
self._type = FileSystemType.UDF
return
from refinery.lib.iso.udf import UDFArchive
udf = UDFArchive()
try:
udf.open(self._data)
except Exception:
return
if udf.refs:
self._udf = udf
self._type = FileSystemType.UDF
elif fs in (FileSystemType.JOLIET, FileSystemType.RR, FileSystemType.ISO):
if self._iso is None:
from refinery.lib.iso.iso9660 import ISO9660Archive
self._iso = ISO9660Archive()
self._iso.open(self._data)
self._iso.select_filesystem(fs)
self._type = self._iso.filesystem_type
def entries(self):
if self._type is FileSystemType.UDF and self._udf is not None:
for ref in self._udf.entries():
yield ISOFile(
path=ref.path,
size=ref.total_size,
date=ref.date,
is_dir=ref.is_dir,
extents=ref.extents,
inline=ref.inline_data,
key=(FileSystemType.UDF, ref),
)
elif self._iso is not None:
for ref in self._iso.entries():
yield ISOFile(
path=ref.path,
size=ref.total_size,
date=ref.date,
is_dir=ref.is_dir,
extents=ref.extents,
key=(FileSystemType.ISO, ref),
)
def extract(self, entry: ISOFile) -> bytes | bytearray:
from refinery.lib.iso.iso9660 import ISORef
from refinery.lib.iso.udf import UDFRef
backend_id, ref = entry.key
if isinstance(ref, ISORef) and (iso := self._iso):
if backend_id != FileSystemType.ISO:
raise ValueError(F'File System Inconsistency; expected ISO, got {backend_id.name}.')
return iso.extract(ref)
if isinstance(ref, UDFRef) and (udf := self._udf):
if backend_id != FileSystemType.UDF:
raise ValueError(F'File System Inconsistency; expected UDF, got {backend_id.name}.')
return udf.extract(ref)
return b''
Sub-modules
refinery.lib.iso.eltorito-
El Torito boot catalog parser for ISO 9660 images.
refinery.lib.iso.iso9660-
ISO 9660 filesystem parser ported from 7zip's Archive/Iso/ implementation.
refinery.lib.iso.udf-
UDF (ECMA-167) filesystem parser ported from 7zip's Archive/Udf/ implementation.
Classes
class FileSystemType (*args, **kwds)-
Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access:
Color.RED
- value lookup:
Color(1)
- name lookup:
Color['RED']
Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Expand source code Browse git
class FileSystemType(enum.Enum): AUTO = 'auto' ISO = 'iso' JOLIET = 'joliet' RR = 'rr' UDF = 'udf'Ancestors
- enum.Enum
Class variables
var AUTO-
The type of the None singleton.
var ISO-
The type of the None singleton.
var JOLIET-
The type of the None singleton.
var RR-
The type of the None singleton.
var UDF-
The type of the None singleton.
class ISOFile (path, size, date, is_dir, extents, key, inline=None)-
Represents a single file entry within an ISO image.
Expand source code Browse git
class ISOFile: """ Represents a single file entry within an ISO image. """ __slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key') def __init__( self, path: str, size: int, date: datetime.datetime | None, is_dir: bool, extents: list[tuple[int, int]], key: tuple[FileSystemType, ISORef | UDFRef], inline: bytes | None = None, ): self.path = path self.size = size self.date = date self.is_dir = is_dir self.extents = extents self._inline = inline self.key = keyInstance variables
var date-
Expand source code Browse git
class ISOFile: """ Represents a single file entry within an ISO image. """ __slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key') def __init__( self, path: str, size: int, date: datetime.datetime | None, is_dir: bool, extents: list[tuple[int, int]], key: tuple[FileSystemType, ISORef | UDFRef], inline: bytes | None = None, ): self.path = path self.size = size self.date = date self.is_dir = is_dir self.extents = extents self._inline = inline self.key = key var extents-
Expand source code Browse git
class ISOFile: """ Represents a single file entry within an ISO image. """ __slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key') def __init__( self, path: str, size: int, date: datetime.datetime | None, is_dir: bool, extents: list[tuple[int, int]], key: tuple[FileSystemType, ISORef | UDFRef], inline: bytes | None = None, ): self.path = path self.size = size self.date = date self.is_dir = is_dir self.extents = extents self._inline = inline self.key = key var is_dir-
Expand source code Browse git
class ISOFile: """ Represents a single file entry within an ISO image. """ __slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key') def __init__( self, path: str, size: int, date: datetime.datetime | None, is_dir: bool, extents: list[tuple[int, int]], key: tuple[FileSystemType, ISORef | UDFRef], inline: bytes | None = None, ): self.path = path self.size = size self.date = date self.is_dir = is_dir self.extents = extents self._inline = inline self.key = key var key-
Expand source code Browse git
class ISOFile: """ Represents a single file entry within an ISO image. """ __slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key') def __init__( self, path: str, size: int, date: datetime.datetime | None, is_dir: bool, extents: list[tuple[int, int]], key: tuple[FileSystemType, ISORef | UDFRef], inline: bytes | None = None, ): self.path = path self.size = size self.date = date self.is_dir = is_dir self.extents = extents self._inline = inline self.key = key var path-
Expand source code Browse git
class ISOFile: """ Represents a single file entry within an ISO image. """ __slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key') def __init__( self, path: str, size: int, date: datetime.datetime | None, is_dir: bool, extents: list[tuple[int, int]], key: tuple[FileSystemType, ISORef | UDFRef], inline: bytes | None = None, ): self.path = path self.size = size self.date = date self.is_dir = is_dir self.extents = extents self._inline = inline self.key = key var size-
Expand source code Browse git
class ISOFile: """ Represents a single file entry within an ISO image. """ __slots__ = ('path', 'size', 'date', 'is_dir', 'extents', '_inline', 'key') def __init__( self, path: str, size: int, date: datetime.datetime | None, is_dir: bool, extents: list[tuple[int, int]], key: tuple[FileSystemType, ISORef | UDFRef], inline: bytes | None = None, ): self.path = path self.size = size self.date = date self.is_dir = is_dir self.extents = extents self._inline = inline self.key = key
class ISOArchive (data)-
Unified ISO/UDF archive reader. Tries UDF first, falls back to ISO 9660.
Expand source code Browse git
class ISOArchive: """ Unified ISO/UDF archive reader. Tries UDF first, falls back to ISO 9660. """ def __init__(self, data): from refinery.lib.iso.udf import ANCHOR_SECTOR, UDFArchive, _verify_tag self._data = data self._iso = None self._udf = None self._type = FileSystemType.ISO udf_found = False dv = memoryview(self._data) for ss in (2048, 512, 4096): anchor_pos = ANCHOR_SECTOR * ss if anchor_pos + 16 <= len(dv): tag_id = _verify_tag(dv, anchor_pos) if tag_id == 2: udf_found = True break if udf_found: udf = UDFArchive() try: udf.open(self._data) except Exception: udf = None if udf is not None and udf.refs: self._udf = udf self._type = FileSystemType.UDF return from refinery.lib.iso.iso9660 import ISO9660Archive iso = ISO9660Archive() iso.open(self._data) self._iso = iso self._type = iso.filesystem_type @property def filesystem_type(self) -> str: return self._type.value def select_filesystem(self, fs: FileSystemType) -> None: if fs is FileSystemType.UDF: if self._udf is not None: self._type = FileSystemType.UDF return from refinery.lib.iso.udf import UDFArchive udf = UDFArchive() try: udf.open(self._data) except Exception: return if udf.refs: self._udf = udf self._type = FileSystemType.UDF elif fs in (FileSystemType.JOLIET, FileSystemType.RR, FileSystemType.ISO): if self._iso is None: from refinery.lib.iso.iso9660 import ISO9660Archive self._iso = ISO9660Archive() self._iso.open(self._data) self._iso.select_filesystem(fs) self._type = self._iso.filesystem_type def entries(self): if self._type is FileSystemType.UDF and self._udf is not None: for ref in self._udf.entries(): yield ISOFile( path=ref.path, size=ref.total_size, date=ref.date, is_dir=ref.is_dir, extents=ref.extents, inline=ref.inline_data, key=(FileSystemType.UDF, ref), ) elif self._iso is not None: for ref in self._iso.entries(): yield ISOFile( path=ref.path, size=ref.total_size, date=ref.date, is_dir=ref.is_dir, extents=ref.extents, key=(FileSystemType.ISO, ref), ) def extract(self, entry: ISOFile) -> bytes | bytearray: from refinery.lib.iso.iso9660 import ISORef from refinery.lib.iso.udf import UDFRef backend_id, ref = entry.key if isinstance(ref, ISORef) and (iso := self._iso): if backend_id != FileSystemType.ISO: raise ValueError(F'File System Inconsistency; expected ISO, got {backend_id.name}.') return iso.extract(ref) if isinstance(ref, UDFRef) and (udf := self._udf): if backend_id != FileSystemType.UDF: raise ValueError(F'File System Inconsistency; expected UDF, got {backend_id.name}.') return udf.extract(ref) return b''Instance variables
var filesystem_type-
Expand source code Browse git
@property def filesystem_type(self) -> str: return self._type.value
Methods
def select_filesystem(self, fs)-
Expand source code Browse git
def select_filesystem(self, fs: FileSystemType) -> None: if fs is FileSystemType.UDF: if self._udf is not None: self._type = FileSystemType.UDF return from refinery.lib.iso.udf import UDFArchive udf = UDFArchive() try: udf.open(self._data) except Exception: return if udf.refs: self._udf = udf self._type = FileSystemType.UDF elif fs in (FileSystemType.JOLIET, FileSystemType.RR, FileSystemType.ISO): if self._iso is None: from refinery.lib.iso.iso9660 import ISO9660Archive self._iso = ISO9660Archive() self._iso.open(self._data) self._iso.select_filesystem(fs) self._type = self._iso.filesystem_type def entries(self)-
Expand source code Browse git
def entries(self): if self._type is FileSystemType.UDF and self._udf is not None: for ref in self._udf.entries(): yield ISOFile( path=ref.path, size=ref.total_size, date=ref.date, is_dir=ref.is_dir, extents=ref.extents, inline=ref.inline_data, key=(FileSystemType.UDF, ref), ) elif self._iso is not None: for ref in self._iso.entries(): yield ISOFile( path=ref.path, size=ref.total_size, date=ref.date, is_dir=ref.is_dir, extents=ref.extents, key=(FileSystemType.ISO, ref), ) def extract(self, entry)-
Expand source code Browse git
def extract(self, entry: ISOFile) -> bytes | bytearray: from refinery.lib.iso.iso9660 import ISORef from refinery.lib.iso.udf import UDFRef backend_id, ref = entry.key if isinstance(ref, ISORef) and (iso := self._iso): if backend_id != FileSystemType.ISO: raise ValueError(F'File System Inconsistency; expected ISO, got {backend_id.name}.') return iso.extract(ref) if isinstance(ref, UDFRef) and (udf := self._udf): if backend_id != FileSystemType.UDF: raise ValueError(F'File System Inconsistency; expected UDF, got {backend_id.name}.') return udf.extract(ref) return b''