dissect.vmfs.vmfs

Module Contents

Classes

VMFS

VMFS filesystem implementation.

FileDescriptor

VMFS FileDescriptor implementation.

BlockStream

Implements a file-like object for VMFS files.

class dissect.vmfs.vmfs.VMFS(volume: BinaryIO | None = None, vh: BinaryIO | None = None, fdc: BinaryIO | None = None, fbb: BinaryIO | None = None, sbc: BinaryIO | None = None, pbc: BinaryIO | None = None, pb2: BinaryIO | None = None, jbc: BinaryIO | None = None)

VMFS filesystem implementation.

Assumes that an LVM has already been loaded.

We implement it quite a bit different from how ESXi seems to use it. ESXi doesn’t really distinguish between disk partitions and LVM volumes. Everything is a VMFS volume, and it either contains a filesystem header at 0x01300000 or it doesn’t. Either it contains an LVM that requires multiple extents or it doesn’t. It’s all VMFS, LVM doesn’t really exist and it’s really just an extra header that specifies if the _filesystem_ requires multiple extents or not.

This doesn’t really fit with how the rest of dissect is architected, so we act like the LVM is a “proper LVM” that exposes a logical volume. We then load a “VMFS filesystem” on top of this logical volume. This volume is made up of one or more extents that are loaded beforehand.

A lot of the math consists of bitwise shifts and masks, which translate to modulo or multiplication operations. For the sake of “maintainability” in relation to the original “code”, we keep this as bitwise masks, at the sacrifice of some human readability. Comments explaining as such are placed where appropriate.

fh = None
descriptor
block_size
major_version
minor_version
uuid
label
resources
file_descriptor
root
property is_vmfs5: bool
property is_vmfs6: bool
get(path: str | int, node: FileDescriptor | None = None) FileDescriptor
iter_fd() collections.abc.Iterator[FileDescriptor]
class dissect.vmfs.vmfs.FileDescriptor(vmfs: VMFS, address: int, name: str | None = None, filetype: int | None = None)

VMFS FileDescriptor implementation.

FileDescriptors are basically the inodes of VMFS and are all located in the fdc.sf resource. They start with heartbeat/lock information that describes their lock state so that multiple ESXi hosts can stay in sync and can’t access files while they’re in use. The actual FileDescriptor struct is at a specific offset (md/metadata alignment) and contains fields that you would expect of an “inode”.

Data is stored in a way that is also similar to many Unix filesystems. There is some space for resident data (actually quite large, between 1-4k) or blocks. The “ZLA” determines how to interpret these blocks. However, they can generally be seperated into two kinds: direct and indirect. Like other filesystems, direct blocks refer directly to the filesystem blocks containg the data, whereas with indirect blocks, you first need to go through one or more layers of indirection go get to the final filesystem block. This is where PB and PB2 addresses come into play.

There are two methods of storing directory data. The “legacy” VMFS5 way and the newer VMFS6 way. The VMFS5 way is really just an array of directory entry structs, but the VMFS6 way is a bit more complex. The data contains blocks aligned on the metadata alignment. Each block contains different data, such as what appears to be a hash table, or a heartbeat/allocation bitmap. Eventually you’ll also get to a block containing actual directory entries, which is once again (mostly) an array of directory entries.

vmfs
address
name = None
__repr__() str
property raw: memoryview

The raw buffer of this file descriptor.

property lock_info: dissect.vmfs.c_vmfs.c_vmfs.FS3_DiskLockInfo

The parsed lock info of this file descriptor.

property descriptor: dissect.vmfs.c_vmfs.c_vmfs.FS3_FileDescriptor

The parsed file descriptor struct for this file descriptor.

property parent: FileDescriptor | None
property size: int

The size of this file.

property type: int

The type of this file.

property zla: int

The ZLA of this file.

property mode: int

The file mode of this file.

property block_size: int

The file specific block size of this file.

property blocks: list[int]

The block array of this file.

property atime: datetime.datetime

The last access time of this file.

property mtime: datetime.datetime

The last modified time of this file.

property ctime: datetime.datetime

The creation time of this file.

The destination of this file, if it’s a symlink.

is_dir() bool

Is this file a directory?

is_file() bool

Is this file a regular file?

Is this file a symlink?

is_system() bool

Is this file a system file?

is_rdm() bool

Is this file a RDM file?

listdir() dict[str, FileDescriptor]

A dictionary of the content of this directory, if this file is a directory.

iterdir() collections.abc.Iterator[FileDescriptor]

Iterate file descriptors of the directory entries, if this file is a directory.

open() io.BytesIO | BlockStream

Open this file and return a new file-like object.

class dissect.vmfs.vmfs.BlockStream(descriptor: FileDescriptor)

Bases: dissect.util.stream.AlignedStream

Implements a file-like object for VMFS files.

VMFS file content can be resident or divided over one or more file blocks. These blocks can be directly or indirectly referenced. If they’re directly referenced, we can read immediately from the referenced block, otherwise we need to go through one or more layers of indirection.

Indirection is implemented using PB or PB2 blocks. The actual indirection algorithm differs slightly between VMFS versions.

Eventual file blocks can also differ between FB (filesystem block), SB (sub/small block) and LFB (large filesystem block).

descriptor
vmfs
blocks
block_size
block_offset_shift
zla