# vdevfs

`vdevfs` is the userspace filesystem frontend for `vdev`. It is built in
`fs/` and mounted with FUSE, usually on `/dev`.

At runtime it does two important things:

- Mirrors the underlying mountpoint content into an in-memory VFS tree.
- Applies `vdev` ACL policy per caller (UID/GID/process/path), so visibility
  and permissions can differ depending on who accesses `/dev`.

## What is it useful for?

Use `vdevfs` when you need more than static UNIX mode bits:

- Hide selected device nodes from specific users/processes.
- Expose the same node with different ownership/mode depending on caller.
- Keep `/dev` operations backed by the underlying filesystem (`*at` calls),
  while still enforcing policy in front.

This is specially useful for desktops, kiosk-like systems, multi-seat, and
mixed trusted/untrusted process environments.

## Practical example

Goal: only root can see `/dev/kvm`; members of `video` can use DRM devices.

1. Configure ACL files in the ACL directory used by your `vdev` config.
2. Start `vdevfs` on the target mountpoint.
3. Verify from different users.

Example ACL files (illustrative):

`10-kvm-root-only.acl`

```ini
[vdev-acl]
paths=^/kvm$
uid=0
setmode=0660
setuid=0
setgid=0
```

`20-dri-video.acl`

```ini
[vdev-acl]
paths=^/dri/(card[0-9]+|renderD[0-9]+)$
gid=video
setmode=0660
setgid=video
```

Basic run command (adjust paths to your setup):

```bash
./build/fs/vdevfs -c /etc/vdev/vdevd.conf /dev
```

Quick checks:

```bash
sudo -u root ls -l /dev/kvm
sudo -u nobody ls -l /dev/kvm   # expected: hidden or denied by policy
sudo -u somevideo ls -l /dev/dri/renderD128
```

## Does this replace `eventfs` in `dev-setup.sh`?

No, not directly.

`vdevfs` and `eventfs` solve different layers:

- `vdevfs`: policy/visibility frontend for device filesystem access.
- `eventfs`: event transport/filesystem used by compatibility event paths
  (for example under `metadata/udev/events/serial`).

`dev-setup.sh` (vdev preseed on every `vdevd` start) prepares the udev metadata
tree, symlinks `/run/udev`, and mounts `eventfs` on the serial events path when
it is not already a **live** FUSE mount (`stat -f '%T'` accepts `fuse`,
`fuseblk`, and `fuse.*`, plus an `ls` probe). If the mount already exists and
responds, preseed does **not** remount (including on `vdev restart`). A stale
but still-mounted FUSE entry is left alone; only system shutdown unmounts via
`vdev-eventfs-umount.sh` (SysV runlevels 0/6). `eventfs` is started under
`setsid` in the background so the preseed shell can exit.

Per [eventfs](https://github.com/jcnelson/eventfs), each **queue directory** under
the mount normally **vanishes when its creator process exits**, unless the
client sets the FUSE xattr `user.eventfs_sticky` on that directory. That is a
**client-side** concern (for example **libudev-compat** right after
`mkdir` on its monitor path), not something `vdevd` or `dev-setup` applies to
the `serial` mountpoint itself:

```sh
setfattr -n user.eventfs_sticky /dev/metadata/udev/events/serial/libudev-$PID-$slot
```

Sticky can let a queue outlive a short-lived creator within the same boot. After
`vdevd` restart, an empty `serial/` is still expected until each client calls
`udev_monitor_new_from_netlink()` again and recreates `libudev-<pid>-<slot>/`
(optionally with the xattr above).

**libudev-compat** consumers use the eventfs-backed queues (`event-put` from
helpers); system **libudev** remains the component that listens on true kernel
netlink uevent sockets when you use stock udev.

If `eventfs` is not mounted (for example the path is already provided some other
way), `vdevfs` is still useful for ACL-based device access control, but it does
not replace that event transport layer.

