Packages
nerves
1.2.0
1.15.0
1.14.3
1.14.2
1.14.1
1.14.0
1.13.2
1.13.1
1.13.0
1.12.0
1.11.3
1.11.2
1.11.1
1.11.0
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.3
1.9.2
retired
1.9.1
1.9.0
1.8.0
1.7.17
1.7.16
1.7.15
1.7.14
retired
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
retired
1.7.1
retired
1.7.0
retired
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.11.0
0.10.1
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.4.0-rc.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
Craft and deploy bulletproof embedded software
Current section
Files
Jump to
Current section
Files
lib/nerves/package/utils/squashfs.ex
defmodule Nerves.Package.Utils.Squashfs do
use GenServer
require Logger
@file_types ["c", "b", "l", "d", "-"]
@device_types ["c", "b"]
@posix [r: 4, w: 2, x: 1, s: 1, t: 1]
@sticky ["s", "t", "S", "T"]
def start_link(rootfs) do
params = unsquashfs(rootfs)
dir =
Path.dirname(rootfs)
|> Path.join("squashfs")
case System.cmd("unsquashfs", [rootfs, "-d", dir]) do
{_result, 0} ->
GenServer.start_link(__MODULE__, [rootfs, dir, params])
{error, _} ->
{:error, error}
end
end
def stop(pid) do
GenServer.call(pid, :stop)
GenServer.stop(pid)
end
def pseudofile(pid) do
GenServer.call(pid, {:pseudofile})
end
def pseudofile_fragment(pid, fragment) do
GenServer.call(pid, {:pseudofile_fragment, fragment})
end
def fragment(pid, fragment, path, opts \\ []) do
GenServer.call(pid, {:fragment, fragment, path, opts})
end
def files(pid) do
GenServer.call(pid, {:files})
end
def merge(pid, file_systems, pseudofiles, path) do
GenServer.call(pid, {:mergefs, file_systems, pseudofiles, path})
end
defp unsquashfs(rootfs) do
case System.cmd("unsquashfs", ["-n", "-ll", rootfs]) do
{result, 0} ->
String.split(result, "\n")
|> parse
{error, _} ->
raise "Error parsing Rootfs: #{inspect(error)}"
end
end
def init([rootfs, stage, params]) do
{:ok,
%{
rootfs: rootfs,
params: params,
stage: stage
}}
end
def handle_call(:stop, _from, s) do
File.rm_rf!(s.stage)
{:reply, :ok, s}
end
def handle_call({:files}, _from, s) do
files =
Enum.reduce(s.params, [], fn
{"d", _, _, _, _}, acc -> acc
{_, file, _, _, _}, acc -> [file | acc]
end)
{:reply, files, s}
end
def handle_call({:pseudofile}, _from, s) do
{:reply, params_to_pseudofile(s.params), s}
end
def handle_call({:pseudofile_fragment, fragment}, _from, s) do
fragment =
Enum.filter(s.params, fn {_, file, _, _, _} ->
file in fragment
end)
{:reply, params_to_pseudofile(fragment), s}
end
def handle_call({:fragment, fragment, path, opts}, _from, s) do
pseudo_fragment =
fragment
|> Enum.map(&path_to_paths/1)
|> List.flatten()
|> Enum.uniq()
pseudo_fragment = Enum.filter(s.params, fn {_, file, _, _, _} -> file in pseudo_fragment end)
fragment = Enum.filter(s.params, fn {_, file, _, _, _} -> file in fragment end)
pseudofile = params_to_pseudofile(pseudo_fragment)
tmp_dir =
Path.dirname(path)
|> Path.join("tmp")
File.mkdir_p!(tmp_dir)
pseudofile_name = opts[:name] || "pseudofile"
pseudofile_path =
Path.dirname(path)
|> Path.join(pseudofile_name)
File.write!(pseudofile_path, pseudofile)
Enum.each(fragment, fn {_, file, _, _, _} ->
src = Path.join(s.stage, file)
dest = Path.join(tmp_dir, file)
Path.dirname(dest)
|> File.mkdir_p!()
File.cp!(src, dest)
end)
IO.puts(path)
System.cmd("mksquashfs", [
tmp_dir,
path,
"-pf",
pseudofile_path,
"-noappend",
"-no-recovery",
"-no-progress"
])
File.rm_rf!(tmp_dir)
# File.rm!(pseudofile_path)
{:reply, {:ok, path}, s}
end
# def handle_call({:mergefs, file_systems, pseudofiles, path}, from, s) do
#
# stage_path =
# s.stage
# |> Path.dirname
#
# unionfs = Path.join(stage_path, "union")
# Enum.each(fs, fn() ->
# System.cmd("unsquashfs", ["-d", s.stage, "-f", fs])
# end)
#
# pseudofile = Enum.reduce(pseudofiles, "", fn(file, acc) ->
# File.read!(file) <> acc <> "\n"
# end)
# pseudofile <> "\n" <> params_to_pseudofile(s.params)
#
# pseudofile_path = Path.join(stage_path, "pseudofile")
# File.write!(pseudofile_path, pseudofile)
#
# System.cmd("mksquashfs", [ststage, path, "-pf", pseudofile_path, "-noappend", "-no-recovery", "-no-progress"])
#
# #File.rm!(pseudofile_path)
#
# {:reply, {:ok, path}, s}
# end
defp params_to_pseudofile(fragment) do
Enum.map(fragment, fn
{type, file, {major, minor}, {p0, p1, p2, p3}, {o, g}} when type in @device_types ->
"#{file} #{type} #{p0}#{p1}#{p2}#{p3} #{o} #{g} #{major} #{minor}"
{_type, file, _attr, {p0, p1, p2, p3}, {o, g}} ->
file = if file == "", do: "/", else: file
"#{file} m #{p0}#{p1}#{p2}#{p3} #{o} #{g}"
end)
|> Enum.reverse()
|> Enum.join("\n")
end
defp parse(_, _ \\ [])
defp parse([], collect), do: collect
defp parse([line | tail], collect) do
collect =
case parse_line(line) do
nil -> collect
value -> [value | collect]
end
parse(tail, collect)
end
defp parse_line(""), do: nil
defp parse_line(<<type::binary-size(1), permissions::binary-size(9), _::utf8, tail::binary>>)
when type in @file_types do
permissions = parse_permissions(permissions)
[own, tail] = String.split(tail, " ", parts: 2)
own = parse_own(own)
tail = String.trim(tail)
{attr, tail} =
if type in @device_types do
[major, tail] = String.split(tail, ",", parts: 2)
tail = String.trim(tail)
[minor, tail] = String.split(tail, " ", parts: 2)
{{major, minor}, tail}
else
[_, tail] = String.split(tail, " ", parts: 2)
{nil, tail}
end
<<_modified::binary-size(16), tail::binary>> = tail
<<"squashfs-root", file::binary>> = String.trim(tail)
file =
if type == "l" do
[file, _] = String.split(file, "->")
String.trim(file)
else
file
end
{type, file, attr, permissions, own}
end
defp parse_line(_), do: nil
defp parse_permissions(<<owner::binary-size(3), group::binary-size(3), other::binary-size(3)>>) do
sticky = 0
sticky = sticky + sticky_to_int(owner, 4) + sticky_to_int(group, 2) + sticky_to_int(other, 1)
{sticky, posix_to_int(owner), posix_to_int(group), posix_to_int(other)}
end
defp parse_own(own) do
[owner, group] = String.split(own, "/")
{owner, group}
end
defp sticky_to_int(<<_::binary-size(1), _::binary-size(1), bit::binary-size(1)>>, weight)
when bit in @sticky,
do: weight
defp sticky_to_int(_, _), do: 0
defp posix_to_int(<<r::binary-size(1), w::binary-size(1), x::binary-size(1)>>) do
Enum.reduce([r, w, x], 0, fn p, a ->
Keyword.get(@posix, String.to_atom(p), 0) + a
end)
end
def path_to_paths(path) do
path
|> Path.split()
|> Enum.reduce(["/"], fn p, acc ->
[h | _t] = acc
[Path.join(h, p) | acc]
end)
|> Enum.uniq()
end
end