# `OpenrelikWorkerCommon.MountUtils.BlockDevice`
[🔗](https://github.com/hacktobeer/openrelik-worker-common-elixir/blob/v0.1.0/lib/openrelik_worker_common/mount_utils.ex#L82)

Provides functionality to map a disk image file to block devices (`loop` or `nbd`)
and mount them (`openrelik_worker_common.mount_utils.BlockDevice`).

The default minimum partition size that gets mounted is 100 MB (`104_857_600` bytes).

# `cmd_result`

```elixir
@type cmd_result() :: %{
  returncode: integer(),
  stdout: String.t() | nil,
  stderr: String.t() | nil
}
```

# `t`

```elixir
@type t() :: %OpenrelikWorkerCommon.MountUtils.BlockDevice{
  blkdevice: String.t() | nil,
  blkdeviceinfo: map() | nil,
  ewf_mount_path: String.t() | nil,
  image_path: String.t(),
  max_mountpath_size: pos_integer(),
  min_partition_size: non_neg_integer(),
  mountpoints: [String.t()],
  mountroot: String.t(),
  opts: keyword(),
  original_image_path: String.t(),
  partitions: [String.t()],
  redis_client: term(),
  redis_lock: OpenrelikWorkerCommon.MountUtils.RedisLock.t() | nil,
  redis_url: String.t(),
  supported_ewftypes: [String.t()],
  supported_fstypes: [String.t()],
  supported_qcowtypes: [String.t()]
}
```

# `blkinfo`

```elixir
@spec blkinfo(t()) :: map()
```

Extracts device and partition information using `lsblk -ba -J`.

# `detach_device`

```elixir
@spec detach_device(t()) :: t()
```

Detaches the block device (`qemu-nbd --disconnect` for NBD devices or `losetup --detach` for loop devices).

# `ewfmount`

```elixir
@spec ewfmount(t()) :: String.t()
```

Mounts an EWF image (`.E01` / `.EX01`) using `ewfmount` and returns `{updated_bd, raw_image_path}`.

# `ewfumount`

```elixir
@spec ewfumount(t()) :: t()
```

Unmounts an EWF FUSE mount and removes its mount directory.

# `get_free_nbd_device`

```elixir
@spec get_free_nbd_device(t()) :: {t(), String.t()}
```

Finds and locks a free NBD device (`/dev/nbd0`..`/dev/nbd10`) in Redis until unlocked or timeout.

Returns `{updated_bd, devname}` (or `devname` when called via `_get_free_nbd_device` override).

# `get_fstype`

```elixir
@spec get_fstype(t(), String.t()) :: String.t()
```

Analyses the file system type of a block device or partition using `blkid`.

# `get_hostname`

```elixir
@spec get_hostname(t() | nil) :: String.t()
```

Returns the hostname from the `NODENAME` environment variable or OS hostname.

# `get_mount_path`

```elixir
@spec get_mount_path(t()) :: String.t()
```

Generates a mount path under `bd.mountroot` respecting `bd.max_mountpath_size`.

# `is_important_partition`

```elixir
@spec is_important_partition(t(), map()) :: boolean()
```

Decides if a partition should be processed (`size >= min_partition_size` and supported fs type).

# `is_supported_fs`

```elixir
@spec is_supported_fs(t(), String.t()) :: boolean()
```

Returns `true` if `devname` points to a block device containing a supported filesystem type.

# `lock_timeout_seconds`

```elixir
@spec lock_timeout_seconds() :: pos_integer()
```

Default Redis lock timeout in seconds (`21600`, 6 hours).

# `losetup`

```elixir
@spec losetup(t()) :: String.t()
```

Maps `bd.image_path` to a loopback device using `losetup`.

# `max_mountpath_size`

```elixir
@spec max_mountpath_size() :: pos_integer()
```

Default maximum mount path character length (`500`).

# `max_nbd_devices`

```elixir
@spec max_nbd_devices() :: pos_integer()
```

Default maximum NBD device index (`10`).

# `min_partition_size_bytes`

```elixir
@spec min_partition_size_bytes() :: pos_integer()
```

Default minimum partition size in bytes (100 MB).

# `mount`

```elixir
@spec mount(t(), String.t() | keyword()) :: {t(), [String.t()]}
```

Mounts a disk or one or more partitions on a mountpoint.

Returns `{updated_bd, mountpoints}`.

# `nbdsetup`

```elixir
@spec nbdsetup(t()) :: String.t()
```

Maps a QCOW image file to an NBD device using `qemu-nbd` and probes partitions with `fdisk`.

# `new`

```elixir
@spec new(String.t(), keyword()) :: t()
```

Initializes a `%BlockDevice{}` struct.

## Options
* `:min_partition_size` - Minimum partition size in bytes (default `104_857_600`).
* `:max_mountpath_size` - Maximum root mount path length (default `500`).
* `:mountroot` - Root directory for mount points (default `"/mnt"`).
* `:redis_url` - Redis URL override (default `System.get_env("REDIS_URL") || "redis://localhost:6379/0"`).
* `:redis_client` - Optional pre-configured Redis client or mock client map.

# `parse_partitions`

```elixir
@spec parse_partitions(t()) :: [String.t()]
```

Parses partition information from `bd.blkdeviceinfo`.

# `required_modules_loaded`

```elixir
@spec required_modules_loaded(t() | nil) :: :ok
```

Checks if the `nbd` kernel module is loaded or if `/dev/nbd*` devices exist.

# `required_tools_available`

```elixir
@spec required_tools_available(t() | nil) :: boolean()
```

Checks if required CLI tools (`lsblk`, `blkid`, `mount`, `qemu-nbd`, `sudo`, `fdisk`, `ntfsinfo`)
are available on `PATH`.

# `select_partitions_to_mount`

```elixir
@spec select_partitions_to_mount(t(), String.t()) :: [String.t()]
```

Selects partitions to mount.

# `setup`

```elixir
@spec setup(t()) :: t()
```

Sets up the `BlockDevice` instance: checks if the image exists, verifies required tools,
configures the block device (`ewfmount`, `qemu-nbd`, or `losetup`), queries `lsblk`,
and scans available partitions.

If an error occurs during setup, any partially attached block device, EWF mount, or
Redis lock is automatically cleaned up before re-raising the original `RuntimeError`.

# `umount`

```elixir
@spec umount(t()) :: t()
```

Unmounts all mounted file systems, detaches the block device, unmounts any EWF FUSE mount,
and releases any Redis lock.

# `umount_all`

```elixir
@spec umount_all(t()) :: t()
```

Unmounts all registered `mountpoints` and removes their directories.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
