Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ sudo cp target/release/fls /usr/local/bin/

### Remote devices over SSH

Use `ssh://[user@]host[:port]/device` as the destination for HTTP/HTTPS or OCI images:
Use `ssh://[user@]host[:port]/device` as the destination for HTTP/HTTPS, OCI, or
local file sources:

```bash
fls from-url "https://example.com/image.img.xz" ssh://root@board/dev/emmc0
fls from-url "oci://quay.io/org/image:latest" ssh://root@board:2222/dev/emmc0
fls from "https://example.com/image.img.xz" ssh://root@board/dev/emmc0
fls from "oci://quay.io/org/image:latest" ssh://root@board:2222/dev/emmc0
fls from ./my.image.simg.xz ssh://root@board/dev/emmc0
# IPv6
fls from-url "https://example.com/image.img.xz" ssh://root@[::1]/dev/emmc0
fls from "https://example.com/image.img.xz" ssh://root@[::1]/dev/emmc0
```

The legacy form `[user@]host:/device` is still accepted (no IPv6 or inline port).
Expand Down Expand Up @@ -97,7 +99,7 @@ skips, matching local flashing; DONE still validates only actual written bytes.
Flash a compressed image from a URL to a block device:

```bash
fls from-url \
fls from \
-k \
-n \
"https://example.com/path/to/image.raw.xz" \
Expand All @@ -108,12 +110,27 @@ fls from-url \
- `-k` - Skip SSL certificate verification (useful for internal servers with self-signed certs)
- `-n` - Print progress on new lines (better for logging)

### Local Files

Flash a local image file (relative or absolute path, `file://`-prefixed or not):

```bash
fls from ./my.image.simg.xz /dev/sdb
fls from /home/user/my.image.simg.xz /dev/sdb
fls from file:///home/user/my.image.simg.xz /dev/sdb
```

`file://` is a literal path prefix, not a URI: `file://./a.img` is the relative
path `./a.img`, and `file:///home/a.img` is `/home/a.img`. Compression is
detected from the file extension (`.gz`, `.xz`, `.zst`). The old `from-url`
command name still works but prints a deprecation warning.

### Advanced Example

Flash with custom headers and progress interval:

```bash
fls from-url \
fls from \
--header "Authorization: Bearer token123" \
--progress-interval 1.0 \
--buffer-size 2048 \
Expand All @@ -125,7 +142,7 @@ fls from-url \

```
Block flash command:
URL: https://example.com/path/to/image.raw.xz
Source: https://example.com/path/to/image.raw.xz
Device: /dev/mmcblk1
Buffer size: 1024 MB
Max retries: 10
Expand Down Expand Up @@ -157,10 +174,10 @@ or to fastboot partitions.

#### Flash an OCI image to a block device

Use `from-url` with an `oci://` prefix:
Use `from` with an `oci://` prefix:

```bash
fls from-url \
fls from \
-u "$REGISTRY_USER" \
-p "$REGISTRY_PASS" \
"oci://quay.io/org/image:latest" \
Expand Down Expand Up @@ -203,22 +220,23 @@ required for authenticated access.

## Command Options

### `fls from-url`
### `fls from`

Flash an image from a URL to a block device.
Flash an image from a URL, OCI image, or local file to a block device.
`from-url` is a deprecated alias that still works.

```
fls from-url [OPTIONS] <URL> <DEVICE>
fls from [OPTIONS] <SOURCE> <DEVICE>
```

**Arguments:**
- `<URL>` - URL to download the image from
- `<DEVICE>` - Destination device path (e.g., `/dev/sdb`, `/dev/mmcblk1`)
- `<SOURCE>` - Image source: `http://`, `https://`, `oci://`, `file://`, or a local path
- `<DEVICE>` - Destination device path (e.g., `/dev/sdb`, `/dev/mmcblk1`) or SSH target (`[user@]host:/dev/emmc0`)

**Options:**
- `-k, --insecure-tls` - Ignore SSL certificate verification
- `--cacert <CACERT>` - Path to CA certificate PEM file for TLS validation
- `--buffer-size <SIZE>` - Buffer size in MB for download buffering (default: 1024)
- `--buffer-size <SIZE>` - Buffer size in MB for input buffering (default: 128)
- `--write-buffer-size <SIZE>` - Write queue and remote unacknowledged DATA limit (default: 128 MiB)
- `--max-retries <NUM>` - Maximum number of retry attempts (default: 10)
- `--retry-delay <SECONDS>` - Delay in seconds between retry attempts (default: 2)
Expand Down
24 changes: 19 additions & 5 deletions src/fls/decompress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,32 @@ fn spawn_decompressor(
Ok((process, cmd))
}

pub(crate) fn get_compression_from_url(url: &str) -> Compression {
let path = url.split('?').next().unwrap_or(url);
let path = path.split('#').next().unwrap_or(path);
let extension = path.rsplit('.').next().unwrap_or("").to_lowercase();
match extension.as_str() {
fn compression_for_extension(extension: &str) -> Compression {
match extension.to_lowercase().as_str() {
"gz" => Compression::Gzip,
"xz" => Compression::Xz,
"zst" | "zstd" => Compression::Zstd,
_ => Compression::None,
}
}

pub(crate) fn get_compression_from_url(url: &str) -> Compression {
let path = url.split('?').next().unwrap_or(url);
let path = path.split('#').next().unwrap_or(path);
compression_for_extension(path.rsplit('.').next().unwrap_or(""))
}

/// Determines the appropriate decompression based on a filesystem path extension.
///
/// The path is treated literally: no query/fragment stripping, no percent
/// decoding, no `~` expansion.
pub(crate) fn get_compression_from_path(path: &std::path::Path) -> Compression {
path.extension()
.and_then(|extension| extension.to_str())
.map(compression_for_extension)
.unwrap_or(Compression::None)
}

type DecompressorResult = (
mpsc::Receiver<Vec<u8>>,
std::thread::JoinHandle<Result<(), String>>,
Expand Down
Loading
Loading