:py:mod:`dissect.vmfs.descriptor` ================================= .. py:module:: dissect.vmfs.descriptor Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: dissect.vmfs.descriptor.DirEntry dissect.vmfs.descriptor.FileDescriptor dissect.vmfs.descriptor.FileDescriptor5 dissect.vmfs.descriptor.FileDescriptor6 dissect.vmfs.descriptor.BlockStream dissect.vmfs.descriptor.BestEffortBlockStream .. py:class:: DirEntry(vmfs: dissect.vmfs.vmfs.VMFS, address: int, name: str, type: dissect.vmfs.c_vmfs.FS3_DescriptorType, raw: dissect.vmfs.c_vmfs.c_vmfs.FS3_DirEntry | dissect.vmfs.c_vmfs.c_vmfs.FS6_DirEntry | None = None) Directory entry representation. :param vmfs: The VMFS instance this directory entry belongs to. :param address: The address of the file descriptor of this directory entry. :param name: The name of the directory entry. :param type: The type of the directory entry. :param raw: The raw directory entry struct, if available. .. py:attribute:: vmfs .. py:attribute:: address .. py:attribute:: name .. py:attribute:: type .. py:attribute:: raw :value: None .. py:method:: __repr__() -> str .. py:property:: file_descriptor :type: FileDescriptor Resolve this directory entry to its file descriptor. .. py:attribute:: fd .. py:class:: FileDescriptor(vmfs: dissect.vmfs.vmfs.VMFS, address: int) VMFS file descriptor implementation. See :class:`FileDescriptor5` and :class:`FileDescriptor6` for the VMFS5 and VMFS6 specific implementations. File descriptors are basically the inodes of VMFS and are all stored in the ``.fdc.sf`` resource. They are the combination of a lock block, a metadata block, and a bit of space for data. They start with lock information, which allows multiple ESXi hosts to stay in sync and place locks. This is followed by the ``FS3_FileMetadata`` structure is and contains fields that you would expect of an "inode". The file descriptor on disk roughly looks like the following: .. code-block:: c struct FS3_FileDescriptor { FS3_DiskLock lockBlock; FS3_FileMetadata metaBlock; char data[N]; }; On VMFS5, each block is 512 bytes large, and there's 1024 bytes of data. On VMFS6, the block size is determined by the metadata alignment. The entire file descriptor is two metadata blocks large, with the lock occupying the first metadata block, and the metadata and data occupying the second metadata block. Data is stored in a way that is also similar to many Unix filesystems. There is is some space at the end of the metadata for either a block pointer array, or some resident data. On VMFS5, the block pointer array and the data portion are stored in the same place, whereas on VMFS6, the block pointer array is aligned to the end of the file descriptor, and the data portion is aligned to the end of the metadata structure. The "zeroLevelAddrType" (or ZLA) determines how to interpret the block pointer array. They can generally be seperated into two kinds: direct and indirect. Like other filesystems, direct blocks refer directly to filesystem blocks, or offsets on disk, that contain data. With indirect blocks, you first need to go through one or more layers of indirection go get to the final filesystem block. View the documentation of :class:`BlockStream` for more information. Directory entries are also stored very differently between VMFS5 and VMFS6. Refer to : func:`FileDescriptor5._iterdir` and :func:`FileDescriptor6._iterdir` for more information on how these work. .. py:attribute:: vmfs .. py:attribute:: address .. py:method:: __repr__() -> str .. py:method:: debug() -> str Return a debug string for this file descriptor. Mimicks ``vmkfstool -D`` output. .. py:method:: from_bytes(vmfs: dissect.vmfs.vmfs.VMFS, address: int, buf: bytes) -> FileDescriptor | FileDescriptor5 | FileDescriptor6 :staticmethod: Create a :class:`FileDescriptor5` or :class:`FileDescriptor6` from a bytes buffer. .. py:property:: raw :type: memoryview The raw buffer of this file descriptor. .. py:property:: lock_info :type: dissect.vmfs.c_vmfs.c_vmfs.FS3_DiskLock The lock info of this file descriptor. .. py:property:: metadata :type: dissect.vmfs.c_vmfs.c_vmfs.FS3_FileMetadata The file metadata of this file descriptor. .. py:property:: data :type: memoryview The data portion of this file descriptor. .. py:property:: blocks :type: list[int] The block array of this file. Also referred to as the pointer array, or data addresses. On VMFS5, this is stored in the data portion of the file descriptor as an array of 32-bit integers. On VMFS6, it's aligned to the end of the file descriptor, and is an array of 64-bit integers. .. py:property:: rdm_mapping :type: dissect.vmfs.c_vmfs.c_vmfs.FS3_RawDiskMap The RDM mapping of this file, if this file is an RDM file. The RDM mapping is stored in the data portion of the file descriptor. .. py:property:: parent :type: FileDescriptor | None The parent file descriptor of this file, if it has one. .. py:property:: size :type: int The size of this file. .. py:property:: type :type: int The type of this descriptor. Not to be confused with the file type. .. py:property:: zla :type: dissect.vmfs.c_vmfs.FS3_ZeroLevelAddrType The "Zero Level Address" type of this file. .. py:property:: mode :type: int The file mode of this file. The mode in the metadata only contains a type bit for directories, we add the appropriate type bits for regular files, symlinks and RDM files. Access the mode through the :attr:`metadata` attribute to get the raw mode value. .. py:property:: block_size :type: int The file specific block size of this file. .. py:property:: atime :type: datetime.datetime The last access time of this file. .. py:property:: mtime :type: datetime.datetime The last modified time of this file. .. py:property:: ctime :type: datetime.datetime The creation time of this file. .. py:property:: link :type: str The destination of this symlink, if this file descriptor is a symlink. .. py:method:: is_dir() -> bool Return whether this file descriptor is a directory. .. py:method:: is_file() -> bool Return whether this file descriptor is a regular file. .. py:method:: is_symlink() -> bool Return whether this file descriptor is a symlink. .. py:method:: is_system() -> bool Return whether this file descriptor is a system file. .. py:method:: is_rdm() -> bool Return whether this file descriptor is an RDM file. .. py:method:: listdir() -> dict[str, DirEntry] A dictionary of the content of this directory, if this file descriptor is a directory. .. py:method:: iterdir() -> collections.abc.Iterator[DirEntry] Iterate file descriptors of the directory entries, if this file descriptor is a directory. .. py:method:: get(name: str) -> DirEntry Get a child directory entry by name. :param name: The name of the directory entry to get. .. py:method:: open() -> BlockStream Open a read-only stream for this file descriptor. .. py:class:: FileDescriptor5(vmfs: dissect.vmfs.vmfs.VMFS, address: int) Bases: :py:obj:`FileDescriptor` VMFS5 file descriptor implementation. .. py:class:: FileDescriptor6(vmfs: dissect.vmfs.vmfs.VMFS, address: int) Bases: :py:obj:`FileDescriptor` VMFS6 file descriptor implementation. .. py:class:: BlockStream(descriptor: FileDescriptor) Bases: :py:obj:`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 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). See :func:`FileDescriptor5._resolve_offset` and :func:`FileDescriptor6._resolve_offset` for more information on how to resolve offsets. .. py:attribute:: descriptor .. py:class:: BestEffortBlockStream(descriptor: FileDescriptor) Bases: :py:obj:`dissect.util.stream.AlignedStream` Implements a file-like object for VMFS files in the case we don't have a volume available. If we don't have a volume handle, we can't read any file blocks on disk, but we can still read resident and sub-block data. This duplicates some code, but it gives us a cleaner implementation of the happy path while still allowing us to read some data in the case we only have a bunch of system files. .. py:attribute:: descriptor