Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F113595
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
17 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/Makefile b/Makefile
index deacaca..55d2e01 100644
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,32 @@
-CFLAGS += -std=c99 -g -Wall -Werror
-CPPFLAGS += -I$(ERL_EI_INCLUDE_DIR) -I/usr/local/include
-LDFLAGS += -L$(ERL_EI_LIBDIR) -L/usr/local/lib
+CFLAGS += -std=c99 -g -Wall
+MAGIC_INCLUDE_DIR = $(MAGIC_DEP_DIR)/src
+MAGIC_LIBDIR = $(MAGIC_DEP_DIR)/src/.libs
+MAGIC_HEADER = $(MAGIC_INCLUDE_DIR)/magic.h
+MAGIC_LIB = $(MAGIC_LIBDIR)/libmagic.so
+CPPFLAGS += -I$(ERL_EI_INCLUDE_DIR) -I$(MAGIC_INCLUDE_DIR)
+LDFLAGS += -L$(ERL_EI_LIBDIR) -L$(MAGIC_LIBDIR)
LDLIBS = -lpthread -lei -lm -lmagic
PRIV = priv/
RM = rm -Rf
ifeq ($(EI_INCOMPLETE),YES)
LDLIBS += -lerl_interface
CFLAGS += -DEI_INCOMPLETE
endif
all: priv/libmagic_port
-priv/libmagic_port: src/libmagic_port.c
+priv/libmagic_port: src/libmagic_port.c $(MAGIC_HEADER) $(MAGIC_LIB)
mkdir -p priv
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@
clean:
$(RM) $(PRIV)
+$(MAGIC_HEADER):
+ $(NOECHO) $(NOOP)
+
+$(MAGIC_LIB):
+ $(NOECHO) $(NOOP)
+
.PHONY: clean
diff --git a/lib/majic.ex b/lib/majic.ex
index b637c0c..7129e6c 100644
--- a/lib/majic.ex
+++ b/lib/majic.ex
@@ -1,119 +1,113 @@
defmodule Majic do
alias Majic.{Once, Pool, Result, Server}
@moduledoc """
Robust libmagic integration for Elixir.
"""
@doc """
Perform on `path`.
An option of `server: ServerName`, `pool: PoolName` or `once: true` must be passed.
"""
@type target :: Path.t() | {:bytes, binary()}
@type result :: {:ok, Result.t()} | {:error, term() | String.t()}
@type name :: {:pool, atom()} | {:server, Server.t()} | {:once, true}
@type option :: name | Server.start_option() | Pool.option()
@spec perform(target(), [option()]) :: result()
def perform(path, opts) do
mod =
cond do
Keyword.has_key?(opts, :pool) -> {Pool, Keyword.get(opts, :pool)}
Keyword.has_key?(opts, :server) -> {Server, Keyword.get(opts, :server)}
Keyword.has_key?(opts, :once) -> {Once, nil}
true -> nil
end
opts =
opts
|> Keyword.drop([:pool, :server, :once])
if mod do
do_perform(mod, path, opts)
else
{:error, :no_method}
end
end
@doc "Compiles a `magic` file or a `Magdir` directory to a magic-compiled database (`.mgc`)"
def compile(path, timeout \\ 5_000) do
compile = fn port ->
send(port, {self(), {:command, :erlang.term_to_binary({:compile_database, path})}})
receive do
{^port, {:data, data}} ->
:erlang.binary_to_term(data)
after
timeout ->
{:error, :timeout}
end
end
with_port(compile, timeout)
end
@doc "Checks whenever a database is valid and can be loaded on the current system."
def valid_database?(path, timeout \\ 5_000) do
valid = fn port ->
send(port, {self(), {:command, :erlang.term_to_binary({:add_database, path})}})
receive do
{^port, {:data, data}} ->
:erlang.binary_to_term(data)
after
timeout ->
{:error, :timeout}
end
end
case with_port(valid, timeout) do
{:ok, _} ->
true
_ ->
false
end
end
defp do_perform({Server = mod, name}, path, opts) do
timeout = Keyword.get(opts, :timeout, Majic.Config.default_process_timeout())
mod.perform(name, path, timeout)
end
defp do_perform({Once = mod, _}, path, opts) do
mod.perform(path, opts)
end
defp do_perform({Pool = mod, name}, path, opts) do
mod.perform(name, path, opts)
end
defp with_port(fun, timeout) do
port =
- Port.open(Majic.Config.get_port_name(), [
- :use_stdio,
- :binary,
- :exit_status,
- {:packet, 2},
- {:args, []}
- ])
+ Port.open(Majic.Config.get_port_name(), Majic.Config.get_port_options(nil))
with_port(port, fun, timeout)
end
defp with_port(port, fun, timeout) do
receive do
{^port, {:data, data}} ->
case :erlang.binary_to_term(data) do
:ready ->
fun.(port)
end
after
timeout ->
{:error, :startup_timeout}
end
after
Port.close(port)
end
end
diff --git a/lib/majic/config.ex b/lib/majic/config.ex
index dabdabd..a9bfc61 100644
--- a/lib/majic/config.ex
+++ b/lib/majic/config.ex
@@ -1,46 +1,46 @@
defmodule Majic.Config do
@moduledoc false
@otp_app Mix.Project.config()[:app]
@executable_name "libmagic_port"
@startup_timeout 1_000
@process_timeout 30_000
@recycle_threshold :infinity
def default_process_timeout, do: @process_timeout
def get_port_name do
{:spawn_executable, to_charlist(get_executable_name())}
end
def get_port_options(_options) do
- [:use_stdio, :binary, :exit_status, {:packet, 2}]
+ [:use_stdio, :binary, :exit_status, {:packet, 2}, {:env, [{'LD_LIBRARY_PATH', :code.priv_dir(:file)}]}]
end
def get_startup_timeout(options) do
get_value(options, :startup_timeout, @startup_timeout)
end
def get_process_timeout(options) do
get_value(options, :process_timeout, @process_timeout)
end
def get_recycle_threshold(options) do
get_value(options, :recycle_threshold, @recycle_threshold)
end
defp get_executable_name do
Path.join(:code.priv_dir(@otp_app), @executable_name)
end
defp get(options, key, default) do
Keyword.get(options, key, default)
end
defp get_value(options, key, default) do
case get(options, key, default) do
value when is_integer(value) and value > 0 -> value
:infinity -> :infinity
_ -> raise ArgumentError, message: "Invalid #{key}"
end
end
end
diff --git a/lib/mix/tasks/compile/majic.ex b/lib/mix/tasks/compile/majic.ex
index 5669754..302cc9b 100644
--- a/lib/mix/tasks/compile/majic.ex
+++ b/lib/mix/tasks/compile/majic.ex
@@ -1,146 +1,151 @@
defmodule Mix.Tasks.Compile.Majic do
use Mix.Task.Compiler
@repo_path Path.join(Mix.Project.deps_path(), "libfile")
@build_path Path.join(Mix.Project.build_path(), "/majic")
@manifest Path.join(Mix.Project.manifest_path(), "/compile.majic")
@patch_path "src/magic_patches"
@built_mgc_path Path.join(to_string(:code.priv_dir(:majic)), "/magic.mgc")
@built_magic_path Path.join(to_string(:code.priv_dir(:majic)), "/magic")
@magic_path Path.join(@repo_path, "/magic")
@magdir Path.join(@magic_path, "/Magdir")
@build_magdir Path.join(@build_path, "/magic")
@shortdoc "Updates and compiles majic's embedded magic database."
@moduledoc """
Uses `libfile` dependency Magdir, applies patches from `#{@patch_path}`, and builds to `#{
@built_mgc_path
}`.
Unlike most Mix compile tasks, this task can be called at runtime from `Majic.Application` to ensure that the built-in
compiled magic database is working on the current system.
"""
defmodule Manifest do
@moduledoc false
defstruct [:hash, {:patches, []}]
end
@impl Mix.Task.Compiler
def clean do
File.rm(@built_mgc_path)
File.rm(@built_magic_path)
File.rm_rf(@build_path)
end
@impl Mix.Task.Compiler
def manifests do
[@manifest]
end
@impl Mix.Task.Compiler
def run(_) do
{:ok, manifest} = read_manifest()
{:ok, sha} = get_dep_revision()
patches = list_patches()
File.mkdir_p!(@build_magdir)
if !File.exists?(@built_mgc_path) || !File.exists?(@built_magic_path) || sha == nil ||
sha != manifest.hash || patches != manifest.patches do
:ok = assemble_magdir()
{:ok, patches, _err} = apply_patches()
:ok = assemble_magic(@built_magic_path)
- {:ok, _} = Majic.compile(@build_magdir)
- File.cp!("magic.mgc", @built_mgc_path)
- File.rm!("magic.mgc")
- manifest = %Manifest{hash: sha, patches: Enum.sort(patches)}
- File.write!(@manifest, :erlang.term_to_binary(manifest))
- Mix.shell().info("Majic: Generated magic database")
+ case Majic.compile(@build_magdir) do
+ {:ok, _} ->
+ File.cp!("magic.mgc", @built_mgc_path)
+ File.rm!("magic.mgc")
+ manifest = %Manifest{hash: sha, patches: Enum.sort(patches)}
+ File.write!(@manifest, :erlang.term_to_binary(manifest))
+ Mix.shell().info("Majic: Generated magic database")
+ :ok
+ {:error, _} ->
+ Mix.shell().error("Majic: Compilation of database failed.")
+ end
end
:ok
end
defp read_manifest do
with {:ok, binary} <- File.read(@manifest),
{:term, %Manifest{} = manifest} <- {:term, :erlang.binary_to_term(binary)} do
{:ok, manifest}
else
{:term, _} -> {:ok, %Manifest{}}
{:error, :enoent} -> {:ok, %Manifest{}}
error -> error
end
end
defp get_dep_revision do
git_dir = Path.join(@repo_path, "/.git")
case System.cmd("git", ["--git-dir=#{git_dir}", "rev-parse", "HEAD"]) do
{rev, 0} -> {:ok, String.trim(rev)}
_ -> {:ok, nil}
end
end
defp list_patches do
@patch_path
|> File.ls!()
|> Enum.filter(fn file ->
Path.extname(file) == ".patch"
end)
|> Enum.sort()
end
defp apply_patches do
patched =
list_patches()
|> Enum.map(fn patch ->
path = Path.expand(Path.join(@patch_path, patch))
# We rewrite paths in patch because we're not applying the patch from repository root, but in a temporary
# build folder that is just the Magdir.
case System.cmd("git", ["apply", "-p3", "--directory=magic/", path], cd: @build_path) do
{_, 0} ->
Mix.shell().info("Majic: Patched magic database: #{patch}")
{patch, :ok}
{error, code} ->
Mix.shell().error("Majic: Failed to apply patch #{patch} (#{code})")
Mix.shell().error(error)
{patch, {:error, error}}
end
end)
ok =
Enum.filter(patched, fn {_, result} -> result == :ok end)
|> Enum.map(fn {name, _} -> name end)
err =
Enum.filter(patched, fn {_, result} -> result != :ok end)
|> Enum.map(fn {name, {:error, error}} -> {name, error} end)
{:ok, ok, err}
end
defp assemble_magdir do
File.rm_rf!(@build_magdir)
File.mkdir!(@build_magdir)
File.ls!(@magdir)
|> Enum.each(fn file ->
File.cp!(Path.join(@magdir, file), Path.join(@build_magdir, file))
end)
:ok
end
defp assemble_magic(destination) do
contents =
File.ls!(@build_magdir)
|> Enum.reduce(<<>>, fn file, acc ->
content = File.read!(Path.join(@build_magdir, file))
<<acc::binary, content::binary>>
end)
File.write!(destination, contents)
:ok
end
end
diff --git a/mix.exs b/mix.exs
index 3658747..27cb395 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,86 +1,89 @@
defmodule Majic.MixProject do
use Mix.Project
if :erlang.system_info(:otp_release) < '21' do
raise "Majic requires Erlang/OTP 21 or newer"
end
def project do
[
app: :majic,
version: "1.0.0",
elixir: "~> 1.7",
elixirc_paths: elixirc_paths(Mix.env()),
elixirc_options: [warnings_as_errors: warnings_as_errors(Mix.env())],
start_permanent: Mix.env() == :prod,
compilers: [:elixir_make] ++ Mix.compilers() ++ [:majic],
make_env: make_env(),
package: package(),
deps: deps(),
dialyzer: dialyzer(),
name: "Majic",
description: "File introspection with libmagic",
source_url: "https://github.com/hrefhref/majic",
docs: docs()
]
end
def application do
[mod: {Majic.Application, []}, extra_applications: [:logger]]
end
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
defp dialyzer do
[
plt_add_apps: [:mix, :iex, :ex_unit, :plug, :mime],
flags: ~w(error_handling no_opaque race_conditions underspecs unmatched_returns)a,
ignore_warnings: "dialyzer-ignore-warnings.exs",
list_unused_filters: true
]
end
defp deps do
[
{:nimble_pool, "~> 0.1"},
{:mime, "~> 1.0"},
+ {:file, github: "hrefhref/file", branch: "master"},
{:plug, "~> 1.0", optional: true},
{:credo, "~> 1.4", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.0.0-rc.6", only: :dev, runtime: false},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
{:elixir_make, "~> 0.6.1", runtime: false},
- {:libfile,
- github: "file/file", branch: "master", app: false, compile: false, runtime: false, sparse: "magic/"}
]
end
defp package do
[
files: ~w(lib/* src/* Makefile),
licenses: ["Apache 2.0"],
links: %{"GitHub" => "https://github.com/hrefhref/majic"},
source_url: "https://github.com/hrefhref/majic"
]
end
defp docs do
[
main: "readme",
extras: ["README.md", "CHANGELOG.md"]
]
end
defp warnings_as_errors(:dev), do: false
defp warnings_as_errors(_), do: true
defp make_env() do
otp =
:erlang.system_info(:otp_release)
|> to_string()
|> String.to_integer()
ei_incomplete = if(otp < 21.3, do: "YES", else: "NO")
- %{"EI_INCOMPLETE" => ei_incomplete}
+
+ magic_dir = Path.join(Mix.Project.deps_path(), "/file")
+
+ %{"EI_INCOMPLETE" => ei_incomplete,
+ "MAGIC_DEP_DIR" => magic_dir}
end
end
diff --git a/mix.lock b/mix.lock
index 09be50c..8d7b401 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,21 +1,22 @@
%{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"},
"dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"},
"earmark": {:hex, :earmark, "1.4.5", "62ffd3bd7722fb7a7b1ecd2419ea0b458c356e7168c1f5d65caf09b4fbdd13c8", [:mix], [], "hexpm", "b7d0e6263d83dc27141a523467799a685965bf8b13b6743413f19a7079843f4f"},
"elixir_make": {:hex, :elixir_make, "0.6.1", "8faa29a5597faba999aeeb72bbb9c91694ef8068f0131192fb199f98d32994ef", [:mix], [], "hexpm", "35d33270680f8d839a4003c3e9f43afb595310a592405a00afc12de4c7f55a18"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"erlexec": {:hex, :erlexec, "1.10.0", "cba7924cf526097d2082ceb0ec34e7db6bca2624b8f3867fb3fa89c4cf25d227", [:rebar3], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.22.1", "9bb6d51508778193a4ea90fa16eac47f8b67934f33f8271d5e1edec2dc0eee4c", [:mix], [{:earmark, "~> 1.4.0", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "d957de1b75cb9f78d3ee17820733dc4460114d8b1e11f7ee4fd6546e69b1db60"},
"exexec": {:hex, :exexec, "0.2.0", "a6ffc48cba3ac9420891b847e4dc7120692fb8c08c9e82220ebddc0bb8d96103", [:mix], [{:erlexec, "~> 1.10", [hex: :erlexec, repo: "hexpm", optional: false]}], "hexpm"},
+ "file": {:git, "https://github.com/hrefhref/file.git", "5303e5dd02c6424f1431f5502a9770b2ce40c3a0", [branch: "master"]},
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
"libfile": {:git, "https://github.com/file/file.git", "3810fb8881131bae5b4c4c2acb0b5464b35ff1ea", [branch: "master", sparse: "magic/"]},
"makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"},
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
"nimble_pool": {:hex, :nimble_pool, "0.2.3", "4b84df87cf8b40c7363782a99faad6aa2bb0811bcd3d275b5402ae4bab1f1251", [:mix], [], "hexpm", "a6bf677d3499ef1639c42bf16b8a72bf490f5fed70206d5851d43dd750c7eaca"},
"plug": {:hex, :plug, "1.10.3", "c9cebe917637d8db0e759039cc106adca069874e1a9034fd6e3fdd427fd3c283", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "01f9037a2a1de1d633b5a881101e6a444bcabb1d386ca1e00bb273a1f1d9d939"},
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 25, 12:09 PM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39782
Default Alt Text
(17 KB)
Attached To
Mode
R20 majic
Attached
Detach File
Event Timeline
Log In to Comment