Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
# Check source.
name: lint
on:
pull_request:
branches:
- main
push:
branches:
- main
on: [pull_request, push]
permissions: read-all
jobs:
black:
Expand Down Expand Up @@ -49,6 +43,26 @@ jobs:
LANG: en_US.UTF-8
run: |
tox -e pylint
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install shellcheck
run: |
sudo apt-get update
sudo apt-get install -y shellcheck
- name: Check format of bash scripts
run: |
find . -name "*.sh" -exec shellcheck {} +
typos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install rumdl
run: cargo install --locked typos-cli
- name: Check for typos
run: |
typos
yamllint:
runs-on: ubuntu-latest
strategy:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_macos.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Run tests on Mac OS.
name: test_macos
on: [push, pull_request]
on: [pull_request, push]
permissions: read-all
jobs:
test_macos:
Expand Down
10 changes: 3 additions & 7 deletions .github/workflows/test_tox.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
# Run tox tests on Ubuntu Docker images using the deadsnakes and GIFT PPAs
name: test_tox
on:
pull_request:
branches:
- main
push:
branches:
- main
on: [pull_request, push]
permissions: read-all
jobs:
build:
Expand All @@ -24,6 +18,8 @@ jobs:
toxenv: 'py313'
- python-version: '3.14'
toxenv: 'py314'
- python-version: '3.15'
toxenv: 'py315'
container:
image: ubuntu:26.04
steps:
Expand Down
12 changes: 4 additions & 8 deletions .github/workflows/test_windows.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Run tests on Windows.
name: test_windows
on: [push, pull_request]
on: [pull_request, push]
permissions: read-all
jobs:
test_windows:
Expand All @@ -20,17 +20,13 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
shell: cmd
run: |
git clone https://github.com/log2timeline/l2tdevtools.git ..\l2tdevtools
set PYTHONPATH=..\l2tdevtools
mkdir downloads
python ..\l2tdevtools\tools\update.py --download-directory downloads --machine-type amd64 --track dev PyYAML dfdatetime dtfabric libbde libcaes libewf libfcrypto libfsapfs libfsext libfsfat libfshfs libfsntfs libfsxfs libfvde libfwnt libluksde libmodi libphdi libqcow libsigscan libsmdev libsmraw libvhdi libvmdk libvsapm libvsgpt libvshadow libvslvm pytsk3 xattr
cache-dependency-path: 'tox.ini'
- name: Install tox
run: |
python -m pip install --upgrade pip
python -m pip install tox tox-gh-actions
- name: Run tests
env:
PIP_CACHE_DIR: ~\AppData\Local\pip\Cache
run: |
tox -e ${{ matrix.toxenv }}
2 changes: 2 additions & 0 deletions .shellcheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
shell=bash
severity=warning
27 changes: 27 additions & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[files]
extend-exclude = [
"test_data/"
]

[default]
binary = false
check-filename = true
check-file = true
unicode = true
locale = "en-us"
extend-ignore-re = [
# Ignore a line that is preceded with `# typos:ignore`
"(#|//)\\s*typos:ignore\\n.*",

# Ignore code blocks between `# typos:disable` and `# typos:enable`
"(?s)(#|//)\\s*typos:disable.*?\\n\\s*(#|//)\\s*typos:enable",

# Ignore "mke2fs"
"\\bmke2fs\\b",

# Ignore "TSK_FS_META_TYPE_WHT"
"\\bTSK_FS_META_TYPE_WHT\\b",
]
extend-words = {
"conectix" = "conectix",
}
4 changes: 2 additions & 2 deletions config/dpkg/changelog
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
dfvfs (20260924-1) unstable; urgency=low
dfvfs (20260927-1) unstable; urgency=low

* Auto-generated

-- Log2Timeline maintainers <log2timeline-maintainers@googlegroups.com> Thu, 24 Sep 2026 07:31:16 +0200
-- Log2Timeline maintainers <log2timeline-maintainers@googlegroups.com> Sun, 27 Sep 2026 08:04:29 +0200
3 changes: 0 additions & 3 deletions config/pylint/spelling-private-dict
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ mft
mmls
msi
mtime
muliple
multi
myfile
myuser
Expand Down Expand Up @@ -155,7 +154,6 @@ serializer
sizehint
sleuthkit
sourcescannode
specificed
sqlite
startup
stderr
Expand All @@ -171,7 +169,6 @@ tarfile
tarinfo
tgz
timestamp
totalling
tsk
tskattribute
tskdatastream
Expand Down
2 changes: 1 addition & 1 deletion dfvfs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
only access to file-system objects from various storage media types and file formats.
"""

__version__ = "20260924"
__version__ = "20260927"
2 changes: 1 addition & 1 deletion dfvfs/analyzer/apm_analyzer_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def GetFormatSpecification(self):
format_specification = specification.FormatSpecification(self.type_indicator)

# APM signature.
# Note that technically "PM" at offset 512 or 2048 is the Apple Partion Map
# Note that technically "PM" at offset 512 or 2048 is the Apple Partition Map
# signature but using the partition type is less error prone.
signature = (
b"Apple_partition_map\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
Expand Down
2 changes: 1 addition & 1 deletion dfvfs/file_io/zip_file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def seek(self, offset, whence=os.SEEK_SET):
self._realign_offset = True

# ZipExtFile tell() is not POSIX compliant hence the current offset
# is tracked seperately.
# is tracked separately.
self._current_offset = offset

def get_offset(self):
Expand Down
2 changes: 1 addition & 1 deletion dfvfs/helpers/source_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ def ScanForVolumeSystem(self, source_path_spec):

type_indicator = type_indicators[0]

# pysigscan found a volume system sigature however for the TSK partition
# pysigscan found a volume system signature however for the TSK partition
# table type we also need the sector size.
scan_for_sector_size = (
type_indicator == definitions.TYPE_INDICATOR_TSK_PARTITION
Expand Down
2 changes: 1 addition & 1 deletion dfvfs/helpers/text_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def readlines(self, sizehint=None):

Args:
sizehint (Optional[int]): maximum byte size to read. If present, instead
of reading up to EOF, whole lines totalling sizehint bytes are read.
of reading up to EOF, whole lines totaling sizehint bytes are read.

Returns:
list[str]: lines of text.
Expand Down
60 changes: 22 additions & 38 deletions dfvfs/lib/raw_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ def _RawGlobPathSpecWithAlphabeticalSchema(
segment_path_spec = path_spec_factory.Factory.NewPathSpec(
parent_path_spec.type_indicator, **kwargs
)

if not file_system.FileEntryExistsByPathSpec(segment_path_spec):
break

Expand Down Expand Up @@ -98,7 +97,6 @@ def _RawGlobPathSpecWithNumericSchema(
segment_path_spec = path_spec_factory.Factory.NewPathSpec(
parent_path_spec.type_indicator, **kwargs
)

if not file_system.FileEntryExistsByPathSpec(segment_path_spec):
break

Expand Down Expand Up @@ -151,7 +149,7 @@ def RawGlobPathSpec(file_system, path_spec):
if not segment_extension:
filename_prefix_length = len(filename_prefix)

# Check if there are muliple segment files in the form: PREFIX[a-z]+
# Check if there are multiple segment files in the form: PREFIX[a-z]+
# where [a-z]+ starts with a and consist of multiple letters,
# e.g. PREFIXaa or PREFIXzz.
if filename_prefix[-2:] == "aa":
Expand All @@ -172,7 +170,7 @@ def RawGlobPathSpec(file_system, path_spec):
upper_case=False,
)

# Check if there are muliple segment files in the form: PREFIX[A-Z]+
# Check if there are multiple segment files in the form: PREFIX[A-Z]+
# where [A-Z]+ starts with A and consist of multiple letters,
# e.g. PREFIXAA or PREFIXZZ.
elif filename_prefix[-2:] == "AA":
Expand All @@ -193,7 +191,7 @@ def RawGlobPathSpec(file_system, path_spec):
upper_case=True,
)

# Check if there are muliple segment files in the form: PREFIX#
# Check if there are multiple segment files in the form: PREFIX#
# where # starts with either 0 or 1 and consist of multiple digits,
# e.g. PREFIX1 or PREFIX000.
elif filename_prefix[-1].isdigit():
Expand Down Expand Up @@ -248,7 +246,7 @@ def RawGlobPathSpec(file_system, path_spec):
else:
segment_files = []

# Check if there are muliple segment files in the form: PREFIX.[a-z]+
# Check if there are multiple segment files in the form: PREFIX.[a-z]+
# where [a-z]+ starts with a and consist of multiple letters,
# e.g. PREFIX.aa or PREFIX.aaa.
elif segment_extension == "a" * segment_extension_length:
Expand All @@ -261,7 +259,7 @@ def RawGlobPathSpec(file_system, path_spec):
upper_case=False,
)

# Check if there are muliple segment files in the form: PREFIX.[A-Z]+
# Check if there are multiple segment files in the form: PREFIX.[A-Z]+
# where [A-Z]+ starts with A and consist of multiple letters,
# e.g. PREFIX.AA or PREFIX.AAA.
elif segment_extension == "A" * segment_extension_length:
Expand All @@ -274,7 +272,7 @@ def RawGlobPathSpec(file_system, path_spec):
upper_case=True,
)

# Check if there are muliple segment files in the form: PREFIX###.asb
# Check if there are multiple segment files in the form: PREFIX###.asb
# where # starts with 1 and consist of multiple digits e.g. PREFIX001.asb.
elif segment_extension == "asb":
if location[-3:] == "001":
Expand All @@ -284,7 +282,7 @@ def RawGlobPathSpec(file_system, path_spec):
else:
segment_files = []

# Check if there are muliple segment files in the form: PREFIX-f###.vmdk
# Check if there are multiple segment files in the form: PREFIX-f###.vmdk
# where # starts with 1 and consist of multiple digits,
# e.g. PREFIX-f001.vmdk.
elif segment_extension == "vmdk":
Expand All @@ -296,26 +294,22 @@ def RawGlobPathSpec(file_system, path_spec):
else:
segment_files = []

# Check if there are muliple segment files in the form: PREFIX.#
# Check if there are multiple segment files in the form: PREFIX.#
# where # starts with either 0 or 1 and consist of multiple digits,
# e.g. PREFIX.1 or PREFIX.000.
elif segment_extension.isdigit():
try:
segment_number = int(segment_extension, 10)
except ValueError:
raise errors.PathSpecError(
(
f"Unsupported path specification invalid segment file extension: "
f"{segment_extension:s}"
)
f"Unsupported path specification invalid segment file extension: "
f"{segment_extension:s}"
)

if segment_number not in [0, 1]:
raise errors.PathSpecError(
(
f"Unsupported path specification invalid segment file extension: "
f"{segment_extension:s}"
)
f"Unsupported path specification invalid segment file extension: "
f"{segment_extension:s}"
)

if segment_extension_length == 1:
Expand All @@ -328,10 +322,8 @@ def RawGlobPathSpec(file_system, path_spec):
segment_format = "{0:s}.{1:04d}"
else:
raise errors.PathSpecError(
(
f"Unsupported path specification invalid segment file extension: "
f"{segment_extension:s}"
)
f"Unsupported path specification invalid segment file extension: "
f"{segment_extension:s}"
)

segment_files = _RawGlobPathSpecWithNumericSchema(
Expand All @@ -341,7 +333,7 @@ def RawGlobPathSpec(file_system, path_spec):
else:
segment_files = []

# Check if there are muliple segment files in the form: PREFIX.#of#
# Check if there are multiple segment files in the form: PREFIX.#of#
# e.g. PREFIX.1of5 - PREFIX.5of5.
segment_number, _, number_of_segments = segment_extension.partition("of")

Expand All @@ -351,25 +343,20 @@ def RawGlobPathSpec(file_system, path_spec):
number_of_segments = int(number_of_segments, 10)
except ValueError:
raise errors.PathSpecError(
(
f"Unsupported path specification invalid segment file "
f"extension: {segment_extension:s}"
)
f"Unsupported path specification invalid segment file "
f"extension: {segment_extension:s}"
)

if segment_number != 1:
raise errors.PathSpecError(
(
f"Unsupported path specification invalid segment file "
f"extension: {segment_extension:s}"
)
f"Unsupported path specification invalid segment file "
f"extension: {segment_extension:s}"
)

for segment_number in range(1, number_of_segments + 1):
segment_location = (
f"{location:s}.{segment_number:d}of{number_of_segments:d}"
)

# Note that we don't want to set the keyword arguments when not used
# because the path specification base class will check for unused
# keyword arguments and raise.
Expand All @@ -382,14 +369,11 @@ def RawGlobPathSpec(file_system, path_spec):
segment_path_spec = path_spec_factory.Factory.NewPathSpec(
parent_path_spec.type_indicator, **kwargs
)

if not file_system.FileEntryExistsByPathSpec(segment_path_spec):
raise errors.PathSpecError(
(
f"Missing segment file: {segment_number:d}of"
f"{number_of_segments:d} for extension: "
f"{segment_extension:s}"
)
f"Missing segment file: {segment_number:d}of"
f"{number_of_segments:d} for extension: "
f"{segment_extension:s}"
)

segment_files.append(segment_path_spec)
Expand Down
2 changes: 1 addition & 1 deletion docs/sources/developer/Helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,5 +152,5 @@ dfvfs/helpers/windows_path_resolver.py
| UNC path | `\\server\share\directory\file.txt` |
| Path with environment variable | `%SystemRoot%\file.txt` |
| Path with trailing number to indicate the corresponding TYPELIB resource inside the PE/COFF | `C:\WINDOWS\PCHealth\HelpCtr\Binaries\HelpCtr.exe\1` |
| Path with volume creation time (FILETIME) and (volume) serial number (found in SuperFetch database and Prefetch files) | `\VOLUME{01d15f816d07ba5e-5e6d77ca}\Windows\System32`
| Path with volume creation time (FILETIME) and (volume) serial number (found in SuperFetch database and Prefetch files) | `\VOLUME{01d15f816d07b85e-5e6d77ca}\Windows\System32`

Loading
Loading