Luigit
repositories / termux-janitor

termux-janitor

Interactive cleanup assistant for Termux: transparent, safe, confirmed disk reclamation.

owned by admin

spec/experiments/shared_storage_capabilities.md

Raw
Rendered preview

Termux shared-storage capability experiment

Question

Can Termux shared-storage aliases be deduplicated by runtime identity, and which filesystem capabilities are established without modifying operator storage?

This tests the scan-root, accounting, and revalidation assumptions in PRODUCT.md.

Constraints

Repository policy forbids tests from modifying operator shared storage. The probe therefore inspected only the shared-storage root and its aliases. It did not enumerate personal content or create, rename, or delete any shared-storage object. Regular-file allocation, sparse-file behavior, and mutation semantics remain untested.

Environment

  • Date: 2026-09-13
  • Kernel: Linux 6.1.176 Android 14, AArch64
  • Shared-storage mount: FUSE at /storage/emulated
  • Mount options observed: rw,nosuid,nodev,noexec,noatime,lazytime,allow_other

Method

A native C probe used statx, statfs, open, openat2, and descriptor-relative fstatat. It inspected these entry points both with normal target resolution and with AT_SYMLINK_NOFOLLOW:

  • ~/storage/shared
  • /sdcard
  • /storage/self/primary
  • /storage/emulated/0

It then opened the physical root with O_PATH | O_DIRECTORY | O_NOFOLLOW, opened . through openat2 with beneath and no-symlink constraints, and repeated descriptor-relative identity checks 1,000 times.

Observations

All resolved aliases reported the same physical identity:

device=0:145 inode=1941 mount_id=9150 type=directory
size=3452 stx_blocks=15 block_size=4096

No-follow inspection showed that the first three entry points are distinct symlinks on other mounts. All resolved to /storage/emulated/0.

statfs type=0x65735546
openat2(".", RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS | RESOLVE_NO_MAGICLINKS)=success
1,000 descriptor-relative checks: stable device and inode

0x65735546 is the Linux FUSE superblock magic value. Android storage documentation describes current emulated shared storage as a FUSE-mediated view.

Conclusion

Runtime physical-root and device/inode identity deduplicate the standard shared-storage aliases on this device. Resolving only configured shared-storage entry-point symlinks during root initialization is therefore supported by the observation. Descriptor-relative access and constrained openat2 lookup work for the physical root.

The experiment does not establish that FUSE stx_blocks represents uniquely reclaimable physical Android storage, that inode identity remains stable across daemon or device lifecycle changes, or that direct mutation has the required semantics. Filesystem type and one successful metadata probe are insufficient grounds for those claims.

The specification therefore requires per-root capability/confidence state. Unknown allocated-block semantics make affected totals incomplete, and missing safety-critical mutation capabilities make findings report-only.

# Termux shared-storage capability experiment

## Question

Can Termux shared-storage aliases be deduplicated by runtime identity, and which filesystem capabilities are established without modifying operator storage?

This tests the scan-root, accounting, and revalidation assumptions in [`PRODUCT.md`](../PRODUCT.md#41-default-scan-roots).

## Constraints

Repository policy forbids tests from modifying operator shared storage.
The probe therefore inspected only the shared-storage root and its aliases.
It did not enumerate personal content or create, rename, or delete any shared-storage object.
Regular-file allocation, sparse-file behavior, and mutation semantics remain untested.

## Environment

- Date: 2026-09-13
- Kernel: Linux 6.1.176 Android 14, AArch64
- Shared-storage mount: FUSE at `/storage/emulated`
- Mount options observed: `rw,nosuid,nodev,noexec,noatime,lazytime,allow_other`

## Method

A native C probe used `statx`, `statfs`, `open`, `openat2`, and descriptor-relative `fstatat`.
It inspected these entry points both with normal target resolution and with `AT_SYMLINK_NOFOLLOW`:

- `~/storage/shared`
- `/sdcard`
- `/storage/self/primary`
- `/storage/emulated/0`

It then opened the physical root with `O_PATH | O_DIRECTORY | O_NOFOLLOW`, opened `.` through `openat2` with beneath and no-symlink constraints, and repeated descriptor-relative identity checks 1,000 times.

## Observations

All resolved aliases reported the same physical identity:

```text
device=0:145 inode=1941 mount_id=9150 type=directory
size=3452 stx_blocks=15 block_size=4096
```

No-follow inspection showed that the first three entry points are distinct symlinks on other mounts.
All resolved to `/storage/emulated/0`.

```text
statfs type=0x65735546
openat2(".", RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS | RESOLVE_NO_MAGICLINKS)=success
1,000 descriptor-relative checks: stable device and inode
```

`0x65735546` is the Linux FUSE superblock magic value.
[Android storage documentation](https://source.android.com/docs/core/storage/scoped) describes current emulated shared storage as a FUSE-mediated view.

## Conclusion

**Runtime physical-root and device/inode identity deduplicate the standard shared-storage aliases on this device.**
Resolving only configured shared-storage entry-point symlinks during root initialization is therefore supported by the observation.
Descriptor-relative access and constrained `openat2` lookup work for the physical root.

The experiment does not establish that FUSE `stx_blocks` represents uniquely reclaimable physical Android storage, that inode identity remains stable across daemon or device lifecycle changes, or that direct mutation has the required semantics.
Filesystem type and one successful metadata probe are insufficient grounds for those claims.

The specification therefore requires per-root capability/confidence state.
Unknown allocated-block semantics make affected totals incomplete, and missing safety-critical mutation capabilities make findings report-only.