Skip to content

Spin42/blob_copy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BlobCopy

Hex.pm License

Copies specific files (firmware blobs, binaries, libraries) from block device partitions to the local filesystem. Handles device readiness, mounting, manifest-driven file copying, and marker-based idempotency.

Designed for embedded Linux systems (e.g. Nerves) where firmware blobs or vendor files need to be copied from a dedicated partition at boot time.

Features

  • Idempotent -- uses a marker file so the copy is a no-op on subsequent boots
  • Manifest-driven -- declaratively specify which files to copy, with glob support
  • Resilient -- waits for block devices to appear and retries mounts, handling early-boot timing issues
  • Partial failure tolerance -- non-critical entries can fail without blocking the operation; only critical: true entries cause overall failure
  • Read-only mounting -- source partition is always mounted read-only
  • Zero runtime dependencies -- pure Elixir library

Installation

Add blob_copy to your list of dependencies in mix.exs:

def deps do
  [
    {:blob_copy, "~> 0.1.0"}
  ]
end

Usage

Basic example

config = %BlobCopy.Config{
  partition: "/dev/mmcblk0p10",
  mount_point: "/tmp/vendor-mount",
  marker_file: "/var/lib/blob-copy-done",
  manifest: [
    %{source: "lib/firmware", dest: "/lib/firmware"},
    %{source: "usr/bin/tool", dest: "/usr/bin/tool", critical: true},
    %{source: "etc/*config*", dest: "/etc"}
  ]
}

BlobCopy.ensure_copied(config)

From application config

# config/config.exs
config :my_app, :blob_copy,
  partition: "/dev/mmcblk0p10",
  mount_point: "/tmp/vendor-mount",
  marker_file: "/var/lib/blob-copy-done",
  manifest: [
    %{source: "lib/firmware", dest: "/lib/firmware", critical: true}
  ]

# In your application code
Application.fetch_env!(:my_app, :blob_copy)
|> BlobCopy.Config.new!()
|> BlobCopy.ensure_copied()

Configuration

BlobCopy.Config accepts the following fields:

Field Type Required Default Description
partition string yes -- Block device path (e.g. "/dev/mmcblk0p10")
mount_point string yes -- Temporary mount point path
marker_file string yes -- Path to idempotency marker file
manifest list yes -- List of file entries to copy
log_prefix string no "BlobCopy" Prefix for log messages
partition_wait_timeout integer no 30_000 Ms to wait for the block device to appear
mount_retries integer no 10 Number of mount retry attempts
mount_retry_delay integer no 1_000 Ms between mount retries

Manifest entries

Each manifest entry is a map with:

Field Type Required Default Description
source string yes -- Path relative to mount point; may contain glob characters (*, ?, [)
dest string yes -- Absolute destination path
permissions integer no 0o755 File permissions (ignored for directory copies)
critical boolean no false If true, failure to copy this entry fails the entire operation

The copy mode is auto-detected per entry:

  • Glob -- source contains *, ?, or [: all matches are copied into dest
  • Directory -- resolved source is a directory: copied recursively
  • File -- single file copy with permissions set

API

  • BlobCopy.ensure_copied(%Config{}) -- idempotent copy operation. Returns :ok or {:error, reason}.
  • BlobCopy.copied?(%Config{}) -- returns true if the marker file exists.

Requirements

  • Linux -- uses mount, umount, mountpoint, cp, and reads /proc/mounts
  • Root privileges (or appropriate capabilities) for mount/unmount operations
  • Elixir >= 1.14

Development

mix deps.get
mix compile
mix docs    # generate documentation
mix test    # run tests

License

MIT -- see LICENSE for details.

About

Copy files and folders in Elixir

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages