Unexpected exception handing
Currently most dissect.\* filesystem projects have their own exceptions.py defintions for e.g. FileNotFoundError classes. These classes are often sub-classed by a local Error class, which in turn is sub-classed by Python's Exception. [1]
This all works fine, except when exceptions are returned from a filesystem project to a 'parent' project such as dissect.target, which has it's own exception definitions which cannot be compared with exceptions from other child projects.
This leads to circumstances where FileNotFoundError != FileNotFoundError in dissect.target - which causes unintentional bugs and unexpected behaviour.
To demonstrate, we recently encountered a bug in dissect.target record_modifier.py's modify_record function. When the Windows MFT contains a reference to a file which no longer exists or does not exist yet on disk, and target-query -f mft --hash is called, dissect would break as FileNotFoundError is not handled here. [2]
try:
_record = modifier_function(field_name, resolved_path)
except FilesystemError:
pass
One would assume adding except (FilesystemError, FileNotFoundError): would fix this. This however does not fix the issue, since we receive dissect.ntfs.exceptions.FileNotFoundError and try to compare it against Python's FileNotFoundError (or dissect.target.exceptions.FileNotFoundError when importing that explicitly). This comparison fails as these exceptions have different underlying types. [3] We could add a generic Exception handler but that would likely be too broad.
Specifically adding NTFS's FileNotFoundError is likely also unwanted, since the modify_record function is not NTFS or MFT specific.
We have identified some possible solutions for this problem:
-
a generic
dissect.exceptionsproject which defines generic exceptions universally for all dissect projects -
preventing filesystem projects to return their own exception classes and use generic Python exceptions instead
-
sub-classing filesystem project exceptions with Python's built-in exceptions
We're interested to learn the reasoning behind separate
exceptions.pyclasses for each project, perhaps there's something fundamental or dissect specific we are misunderstanding here.[1] https://github.com/fox-it/dissect.ntfs/blob/main/dissect/ntfs/exceptions.py [2] https://github.com/fox-it/dissect.target/blob/main/dissect/target/helpers/record_modifier.py#L81 [3] https://docs.python.org/3/library/exceptions.html