Zygote
Storagenerd

vold and filesystems

The volume daemon, the filesystems it mounts, adoptable storage, encryption and the 16 KB page change — the layer under /data that almost nobody sees.

11 minAPI 1–37 · 17interactivequizverified 2026-10-27

Under every app's files is a small native daemon that decides which volumes exist, which filesystem each one uses, how it is encrypted, and who can mount it. vold is one of the least visible and most consequential parts of the storage stack.

TL;DR#

  • vold is the volume daemon: it detects, formats, mounts and unmounts storage volumes.
  • userdata is typically ext4 or f2fs; read-only system partitions increasingly use EROFS.
  • File-based encryption (FBE) encrypts each file with a key derived from the user's credential, enabling direct boot.
  • Adoptable storage (API 23Android 6.0) formats an SD card as an extension of internal storage.
  • 16 KB page sizes (API 35Android 15) change the granularity of every filesystem and require native libraries to be aligned.

Mental model#

Think of a building's facilities manager.

They decide which storerooms exist, what kind of shelving each has, which key opens which door, and what happens when a storeroom is added or removed. Tenants never meet them; they just find that the shelves are the right height and the door opens.

vold is that manager. The kernel provides the filesystem implementations; vold decides how they are used.

Where the analogy breaks#

A facilities manager works with physical rooms. vold works with block devices that may be encrypted, may be on a removable card, and may be mounted differently for each user on the device. Multi-user makes "the" storage a set of per-user views over shared blocks.

The big picture#

Loading diagram…
Text description of this diagram

A treemap of the storage stack's consumers: app-private data, shared media, physical and emulated volumes, and read-only system partitions. Each group is mounted differently — encrypted per-user, FUSE-mediated, or read-only — and each has its own filesystem and access rules.

The treemap is an app's view. Underneath it, each group maps to a different mount with a different filesystem and a different encryption key.

Deep dive#

What vold does#

vold is a native daemon started by init. Its responsibilities:

  • Watch for block devices appearing and disappearing (uevent).
  • Format a volume when asked.
  • Mount and unmount volumes, with the right flags and the right UID/GID mapping.
  • Manage the encryption keys for adopted and internal storage.
  • Notify the framework of state changes.

The framework's StorageManagerService talks to vold over Binder. Apps never talk to vold directly; they see volumes through StorageManager and paths through Environment.1

Filesystems#

FilesystemWhereWhy
ext4userdata on many devicesmature, journaled, well understood
f2fsuserdata on many devicesflash-optimised, better random write behaviour
EROFSread-only system partitionscompressed, read-only, fast mount
FAT/exFATremovable SD cardsinteroperability with other devices

The choice between ext4 and f2fs is a device decision, often made by the SoC vendor. Both are journaled and both support the platform's encryption.

EROFS is the interesting one. It is a read-only filesystem designed for compressed system images: smaller images, faster mounts, and no write path to maintain. Its adoption for system partitions is one reason OTA images have shrunk even as the platform has grown.2

Adoptable storage#

API 23Android 6.0 added adoptable storage: an SD card can be formatted as an extension of internal storage, encrypted with the device key, and treated as part of userdata.

The trade is real:

  • Pro: more internal storage, apps can be moved to it, and it behaves like internal storage.
  • Con: the card is no longer readable by other devices, and removing it can break the device until it is reinserted or the adopted storage is forgotten.

Most devices present adoptable storage as an option rather than the default, and many OEMs hide it.

File-based encryption#

Older Android used full-disk encryption (FDE): the whole userdata partition was decrypted with one key at boot. The problem was that the device could not boot far enough to do anything useful until the user entered the credential.

File-based encryption (FBE) encrypts each file with a key derived from a master key, and the master key is available in two states: device encrypted (available before the user unlocks) and credential encrypted (available after).3

This is what makes direct boot possible: an alarm can fire, a phone call can arrive, and a boot-complete receiver can run before the user has entered their PIN, because those components only need device-encrypted storage.3

Metadata encryption#

FBE encrypts file contents but leaves directory structure and file names visible in the clear unless metadata encryption is enabled. Metadata encryption wraps the whole filesystem in an additional layer so that names and inodes are protected too.4

The combination — metadata encryption for the structure, FBE for the contents — is the modern default on devices that support it.

The 16 KB page change#

API 35Android 15 introduced support for devices with 16 KB memory pages, up from the 4 KB that ARM devices have used for years.5

Page size is not a filesystem feature, but it affects filesystems directly:

  • The minimum allocation unit becomes 16 KB, so small files waste more space.
  • Native libraries must be aligned to 16 KB or they cannot be mapped.
  • Apps with native code must be rebuilt with the right alignment and tested on a 16 KB device.5

The change is transparent to Kotlin and Java code and disruptive to anything with a .so. It is a good example of a kernel-level decision that surfaces as an app compatibility requirement.

Multi-user and the per-user view#

Android supports multiple users (a work profile is a user). Each user gets their own userdata subtree, their own credential-encrypted keys, and their own view of /storage/emulated/<userId>.

vold mounts the per-user storage when the user starts and unmounts it when the user stops. A file written by one user is not visible to another, even though both live on the same physical partition.

Why storage bugs are hard#

The storage stack has more moving parts than almost any other: a kernel filesystem, a userspace daemon, a FUSE layer, an encryption layer, a per-user mount namespace, and the framework's abstractions on top. A bug can be in any of them, and the symptom — "the file is not there" — is the same for all.

Why vold is a separate daemon#

Mounting a filesystem is a privileged operation. Putting it in system_server would mean every mount ran with the framework's privileges and blocking the framework's main thread.

vold is a small, single-purpose daemon with a narrow Binder interface. It runs as root, mounts what it is asked to mount, and reports state. The framework decides what to mount; vold decides how.

That split is also why storage bugs can be hard to attribute: a failure to mount surfaces in the framework as a missing volume, with the cause in a different process.

f2fs versus ext4 in practice#

Both are journaled and both work. The choice is usually made by the SoC vendor based on the storage hardware:

  • f2fs is designed for flash: it reduces write amplification and improves random write behaviour on the kind of NAND a phone uses.
  • ext4 is more mature and better understood, with more tooling.

An app cannot tell which is in use, and should not care: the contract is POSIX file semantics either way.

EROFS and the system image#

EROFS is read-only and compressed. For a system partition that never changes at runtime, that is exactly the right trade: a smaller image, a faster mount, and no need for a journal.

The consequence for the platform is that OTA images are smaller and boot is faster, which is why its adoption spread across system partitions.

Direct boot and the app's obligations#

An app that wants to run before unlock must:

  • Mark the component directBootAware in the manifest.
  • Store the data it needs before unlock in device-encrypted storage.
  • Handle the transition to credential-encrypted storage after unlock.

The transition is the hard part: the app may be running in one storage state and then gain access to another. The LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts mark the two moments.

Why storage is a security boundary#

The storage stack is where several security properties meet: the per-user encryption keys, the SELinux labels on the mount points, the FUSE daemon's per-app view, and the framework's permission checks.

A bug in any layer can expose another app's data, which is why the layers are deliberately redundant — the kernel's UID check, the FUSE policy and the framework's permission check all have to pass.

How it evolved#

API 14 →Full-disk encryptionone key for the whole partitionAPI 14
API 23 →Adoptable storagean SD card joins internal storageAPI 23
API 24 →File-based encryptionper-file keys and direct bootAPI 24
API 35 →16 KB pageslarger page granularityAPI 35
Storage changes10 releases
APIVersionWhat changed
31.5 Cupcake
  • android.widget video and media improvements
123.1 Honeycomb
  • ParcelFileDescriptor improvements
154.0.3 Ice Cream Sandwich
  • New calendar and media provider APIs
194.4 KitKat
  • Storage Access Framework
247.0 Nougat
  • FileProvider and StrictMode for file URLs
2910
  • Scoped storage with requestLegacyExternalStorage opt-out
3011
  • Scoped storage enforced for targeting apps
3313 Tiramisu
  • READ_MEDIA_IMAGES/VIDEO/AUDIO replace storage reads
3414 Upside Down Cake
  • READ_MEDIA_VISUAL_USER_SELECTED
3717 Cinnamon Bun
  • ACTION_PICK_CONTACTS gives session-scoped contact field access

Under the hood#

Curiosity corner#

  • Adopted storage can brick a device's data if the card fails. The card holds part of userdata, and there is no redundancy.
  • EROFS is read-only by design. That is not a limitation for a system partition; it is the point.
  • A 16 KB page device can run a 4 KB-built app, but not if the app ships native libraries that are misaligned.

Try it#

Mounted filesystems
adb shell mount | grep -E "data|system"
Example output
/dev/block/dm-4 on /data type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime)
/dev/block/dm-2 on /system type erofs (ro,seclabel,relatime)
Volume state
adb shell sm list-volumes all
Example output
emulated;0 mounted
public:179,1 mounted
Encryption state
adb shell dumpsys mount | grep -i "encrypt" | head
Example output
state: encrypted
type: file

Check yourself#

Check yourself

1 / 12

Why did WRITE_EXTERNAL_STORAGE become a privacy problem?

0 answered · 0 correct

Sources#

Sources

Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.

  1. docsStorage overviewsource.android.comaccessed 2026-10-27↩
  2. docsEROFS filesystemsource.android.comaccessed 2026-10-27↩
  3. docsFile-Based Encryption (FBE)source.android.comaccessed 2026-10-27↩
  4. docsMetadata encryptionsource.android.comaccessed 2026-10-27↩
  5. docs16 KB page size: developer optionsource.android.comaccessed 2026-10-27↩
  6. docsPerubahan perilaku: aplikasi yang menargetkan API level 28+developer.android.comaccessed 2026-10-27↩