mtp-mount 0.3.0

Mount MTP devices as local filesystems via FUSE
Documentation
[![Crate](https://img.shields.io/crates/v/mtp-mount.svg)](https://crates.io/crates/mtp-mount)
[![License](https://img.shields.io/crates/l/mtp-mount.svg)](https://github.com/vdavid/mtp-mount)
[![CI](https://img.shields.io/github/actions/workflow/status/vdavid/mtp-mount/ci.yml?label=CI)](https://github.com/vdavid/mtp-mount/actions)

# mtp-mount

Mount MTP devices as local filesystems via FUSE.

Plug in your Android phone or camera, run `mtp-mount /mnt/phone`, and use
`ls`, `cp`, `cat`, `rm`, etc. on your device's storage. Built on
[`mtp-rs`](https://crates.io/crates/mtp-rs) (pure Rust, no libmtp) and
[`fuser`](https://crates.io/crates/fuser).

## Install

```sh
cargo install mtp-mount
```

## Usage

Mount the first available MTP device:

```sh
mtp-mount /mnt/phone
```

Pick a specific device by serial number:

```sh
mtp-mount -d SERIAL /mnt/phone
```

Mount read-only:

```sh
mtp-mount -r /mnt/phone
```

Unmount:

```sh
umount /mnt/phone           # Linux
diskutil unmount /mnt/phone  # macOS
```

Run `mtp-mount --help` for the full list of options.

## Supported operations

- **Read**: `cat`, `cp`, `head`, `less`, random-access seeks (media scrubbing, `tail -c`, partial `dd`)
- **Write**: create files, overwrite existing files
- **Directories**: `ls`, `mkdir`, `rmdir`
- **Delete**: `rm`
- **Rename/move**: `mv`

## Not supported

MTP is an object-based protocol, not a block device. Some POSIX features
don't map:

- Hardlinks and symlinks
- File permissions (`chmod`/`chown` are no-ops, everything shows as 0644/0755)
- Extended attributes
- Sparse files or random-access writes (files are uploaded whole on close)

## How it works

The FUSE layer translates filesystem calls into MTP operations:

- **Reads** are byte-range on-demand: each FUSE `read(offset, size)` fetches only the missing bytes via MTP's `GetPartialObject64`, writes them into a sparse tempfile, and serves the requested slice. Repeated reads of the same region hit the cache. Works with files larger than 4 GB.
- **Writes** buffer to a temp file, then flush to the device on close
- **Overwrites** use a safe upload-then-delete-then-rename sequence when the device supports rename, so data is never lost if the upload fails
- **Directory listings** are cached and refreshed on `opendir`. A background event monitor listens for device-side changes (files added, removed, or modified on the device itself) and invalidates the cache automatically

## Requirements

You need a FUSE implementation:

- **Linux**: `sudo apt install libfuse3-dev` (Debian/Ubuntu) or `fuse3` (Fedora/Arch)
- **macOS**: [macFUSE]https://osxfuse.github.io/ or [FUSE-T]https://www.fuse-t.org/ (may need manual `pkg-config` wiring)

## Build from source

```sh
git clone https://github.com/vdavid/mtp-mount.git
cd mtp-mount
cargo build --release
```

## Testing

Unit tests run without FUSE:

```sh
cargo test
```

Integration tests mount a virtual MTP device via FUSE (Linux only, needs `libfuse3-dev`):

```sh
cargo test --test integration -- --ignored --test-threads=1
```

All 63 tests (42 unit + 21 integration) pass on Linux. The integration tests
use `mtp-rs`'s virtual device transport, so no physical device is needed.

## License

MIT OR Apache-2.0