Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F112818
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
29 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7f87bf7..c5e1346 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,33 +1,33 @@
-image: elixir:1.10-alpine
+image: elixir:1.17-alpine
variables:
MIX_ENV: test
GIT_SUBMODULE_STRATEGY: recursive
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- deps
- _build
stages:
- test
- publish
before_script:
- apk add build-base cmake libmagic file-dev
- mix local.hex --force
- mix local.rebar --force
- mix deps.get --only test
- mix compile --force
lint:
stage: test
script:
- mix format --check-formatted
unit-testing:
stage: test
coverage: '/(\d+\.\d+\%) \| Total/'
script:
- mix test --trace --preload-modules --cover
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bebb05d..585f87a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,62 +1,69 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog][1], and this project adheres to [Semantic Versioning][2].
[1]: https://keepachangelog.com/en/1.0.0/
[2]: https://semver.org/spec/v2.0.0.html
-## majic [Unreleased]
+## majic [1.1.0]
+
+- Updated dependencies
+- `-Werror` removed from `Makefile`
+
+## majic [1.0.0]
## Added
- Forked gen_magic.
-- Pool: `Majic.Pool`
-- Plug: `Majic.Plug`
-- Unified API: `Majic.perform/1,2,3`
+- Pool: `Majic.Pool`, using [nimble_pool](https://hex.pm/packages/nimble_pool).
+- Plug: `Majic.Plug`.
+- Unified API: `Majic.perform/1,2,3`.
+- Builds an up-to-date and patched magic database.
+- `Majic.compile/2`
+- `Majic.Server.reload/2,3`
+- `Majic.Server.recycle/2,3`
## Changed
-- C port now using erl_interface
+- Improved C port, now using erl_interface
- Builds on Musl
- Better error and timeout handling
-- `Majic.Server.reload/2,3`
-- `Majic.Server.recycle/2,3`
- Bytes support: `Majic.Server.perform(ref, {:bytes, <<>>})`
- Renamed `priv/apprentice` to `priv/libmagic_port` to be more obvious in `ps`
- Renamed `Majic.Helpers.perform_once` to `Majic.Once.perform`
## gen_majic [1.0]
### Added
- Added support for process recycling (evadne).
- Added documentation (evadne).
### Changed
- Replaced GenServer with `:gen_statem` (evadne).
- Changed API; added support for customisation.
- Refined tests and other aspects of the library (evadne).
## [0.20.83]
### Added
- Soak testing script (devstopfix)
### Changed
- Replaced Erlexec usage with Port (devstopfix)
## 0.0.1
### Added
- Initial Elixir wrapper with Erlexec (evadne)
- Intiial C program (evadne)
[unreleased]: https://github.com/evadne/gen_magic/compare/develop
[0.20.83]: https://github.com/devstopfix/gen_magic/commit/7e27fd094cb462d26ba54fde0205a5be313d12da
diff --git a/Makefile b/Makefile
index deacaca..edfdf18 100644
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,25 @@
-CFLAGS += -std=c99 -g -Wall -Werror
+CFLAGS += -std=c99 -g -Wall
CPPFLAGS += -I$(ERL_EI_INCLUDE_DIR) -I/usr/local/include
LDFLAGS += -L$(ERL_EI_LIBDIR) -L/usr/local/lib
-LDLIBS = -lpthread -lei -lm -lmagic
+LDLIBS = -lpthread
PRIV = priv/
RM = rm -Rf
ifeq ($(EI_INCOMPLETE),YES)
LDLIBS += -lerl_interface
CFLAGS += -DEI_INCOMPLETE
endif
+LDLIBS += -lei -lm -lmagic
+
+
all: priv/libmagic_port
priv/libmagic_port: src/libmagic_port.c
mkdir -p priv
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@
clean:
$(RM) $(PRIV)
.PHONY: clean
diff --git a/mix.exs b/mix.exs
index b360433..ad32f12 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,83 +1,84 @@
defmodule Majic.MixProject do
use Mix.Project
- if :erlang.system_info(:otp_release) < '21' do
+ if :erlang.system_info(:otp_release) < ~c"21" do
raise "Majic requires Erlang/OTP 21 or newer"
end
def project do
[
app: :majic,
- version: "1.0.0",
+ version: "1.1.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(),
make_env: make_env(),
package: package(),
deps: deps(),
dialyzer: dialyzer(),
name: "Majic",
description: "File introspection with libmagic",
source_url: "https://git.pleroma.social/pleroma/elixir-libraries/majic",
- docs: docs()
+ docs: docs(),
+ test_coverage: [summary: false]
]
end
def application do
[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.2"},
+ {:nimble_pool, "~> 1.0"},
{:mime, "~> 1.0"},
{: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}
+ {:elixir_make, "~> 0.8.4", runtime: false}
]
end
defp package do
[
licenses: ["Apache 2.0"],
links: %{"GitHub" => "https://github.com/hrefhref/majic"},
source_url: "https://git.pleroma.social/pleroma/elixir-libraries/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}
end
end
diff --git a/mix.lock b/mix.lock
index c173957..a83cdca 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,22 +1,23 @@
%{
- "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"},
+ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
+ "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"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"},
- "earmark_parser": {:hex, :earmark_parser, "1.4.18", "e1b2be73eb08a49fb032a0208bf647380682374a725dfb5b9e510def8397f6f2", [:mix], [], "hexpm", "114a0e85ec3cf9e04b811009e73c206394ffecfcc313e0b346de0d557774ee97"},
- "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
- "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
+ "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
+ "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"},
+ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"erlexec": {:hex, :erlexec, "1.10.0", "cba7924cf526097d2082ceb0ec34e7db6bca2624b8f3867fb3fa89c4cf25d227", [:rebar3], [], "hexpm"},
- "ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"},
+ "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"},
"exexec": {:hex, :exexec, "0.2.0", "a6ffc48cba3ac9420891b847e4dc7120692fb8c08c9e82220ebddc0bb8d96103", [:mix], [{:erlexec, "~> 1.10", [hex: :erlexec, repo: "hexpm", optional: false]}], "hexpm"},
- "jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
- "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
- "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"},
- "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
- "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
- "nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"},
- "nimble_pool": {:hex, :nimble_pool, "0.2.4", "1db8e9f8a53d967d595e0b32a17030cdb6c0dc4a451b8ac787bf601d3f7704c3", [:mix], [], "hexpm", "367e8071e137b787764e6a9992ccb57b276dc2282535f767a07d881951ebeac6"},
- "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_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
+ "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
+ "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
+ "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
+ "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"},
+ "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
+ "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
+ "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
+ "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
+ "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
+ "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
}
diff --git a/src/libmagic_port.c b/src/libmagic_port.c
index fcfd72a..84199e3 100644
--- a/src/libmagic_port.c
+++ b/src/libmagic_port.c
@@ -1,447 +1,449 @@
//
// libmagic_port: The Sorcerer’s Apprentice
//
// To use this program, compile it with dynamically linked libmagic, as mirrored
// at https://github.com/file/file. You may install it with apt-get,
// yum or brew. Refer to the Makefile for further reference.
//
// This program is designed to run interactively as a backend daemon to the
// GenMagic library.
//
// Communication is done over STDIN/STDOUT as binary packets of 2 bytes length
// plus X bytes payload, where the payload is an erlang term encoded with
// :erlang.term_to_binary/1 and decoded with :erlang.binary_to_term/1.
//
// Once the program is ready, it sends the `:ready` atom.
//
// It is then up to the Erlang side to load databases, by sending messages:
// - `{:add_database, :default | path}`
//
// If the requested database have been loaded, an `{:ok, :loaded}` message will
// follow. Otherwise, the process will exit (exit code 1).
//
// Commands are sent to the program STDIN as an erlang term of `{Operation,
// Argument}`, and response of `{:ok | :error, Response}`.
//
// The program may exit with the following exit codes:
// - 1 if libmagic handles could not be opened,
// - 2 if something went wrong with ei_*,
// - 3 if you sent invalid term format,
// - 255 if the loop exited unexpectedly.
//
// Invalid packets will cause the program to exit (exit code 3). This will
// happen if your Erlang Term format doesn't match the version the program has
// been compiled with.
//
// Commands:
// {:reload, _} :: :ready
// {:add_database, :default | String.t()} :: {:ok, _} | {:error, _}
// {:file, path :: String.t()} :: {:ok, {type, encoding, name}} | {:error,
// :badarg} | {:error, {errno :: integer(), String.t()}}
// {:bytes, binary()} :: same as :file
// {:stop, reason :: atom()} :: exit 0
#include <arpa/inet.h>
#include <ei.h>
#ifdef EI_INCOMPLETE
#include <erl_interface.h>
#endif
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <magic.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define ERROR_OK 0
#define ERROR_MAGIC 1
#define ERROR_EI 2
#define ERROR_BAD_TERM 3
// We use a bigger than possible valid command length (around 4111 bytes) to
// allow more precise errors when using too long paths.
#define COMMAND_LEN 8000
#define COMMAND_BUFFER_SIZE COMMAND_LEN + 1
#define MAGIC_FLAGS_COMMON (MAGIC_CHECK | MAGIC_ERROR)
magic_t magic_setup(int flags);
#define EI_ENSURE(result) \
do { \
if (result != 0) { \
fprintf(stderr, "EI ERROR, line: %d", __LINE__); \
exit(ERROR_EI); \
} \
} while (0);
typedef char byte;
void setup_environment();
void magic_close_all();
void magic_open_all();
int magic_load_all(char *path);
void process_command(uint16_t len, byte *buf);
void process_command_file(byte *buf, int index, ei_x_buff *result);
void process_command_bytes(byte *buf, int index, ei_x_buff *result);
void process_command_load(byte *buf, int index, ei_x_buff *result);
void process_file(char *path, ei_x_buff *result);
void process_bytes(char *bytes, int size, ei_x_buff *result);
void process_load(ei_x_buff *result, char *path);
void send_and_free(ei_x_buff *result);
size_t read_cmd(byte *buf);
size_t write_cmd(byte *buf, size_t len);
void error(ei_x_buff *result, const char *error);
void handle_magic_error(magic_t handle, int errn, ei_x_buff *result);
void fdseek(uint16_t count);
static magic_t magic_mime_type; // MAGIC_MIME_TYPE
static magic_t magic_mime_encoding; // MAGIC_MIME_ENCODING
static magic_t magic_type_name; // MAGIC_NONE
bool magic_loaded = false;
int main(int argc, char **argv) {
#ifdef EI_INCOMPLETE
erl_init(NULL, -1);
#else
EI_ENSURE(ei_init());
#endif
setup_environment();
magic_open_all();
byte buf[COMMAND_BUFFER_SIZE];
uint16_t len;
while ((len = read_cmd(buf)) > 0) {
process_command(len, buf);
}
return 255;
}
void process_command(uint16_t len, byte *buf) {
ei_x_buff result;
char atom[128];
int index, version, arity;
index = 0;
// Initialize result
EI_ENSURE(ei_x_new_with_version(&result));
EI_ENSURE(ei_x_encode_tuple_header(&result, 2));
if (len >= COMMAND_LEN)
return error(&result, "badarg");
if (ei_decode_version(buf, &index, &version) != 0)
exit(ERROR_BAD_TERM);
if (ei_decode_tuple_header(buf, &index, &arity) != 0)
return error(&result, "badarg");
if (arity != 2)
return error(&result, "badarg");
if (ei_decode_atom(buf, &index, atom) != 0)
return error(&result, "badarg");
// {:file, path}
if (strlen(atom) == 4 && strcmp(atom, "file") == 0)
return process_command_file(buf, index, &result);
// {:bytes, bytes}
if (strlen(atom) == 5 && strcmp(atom, "bytes") == 0)
return process_command_bytes(buf, index, &result);
// {:add_database, path}
if (strlen(atom) == 12 && strcmp(atom, "add_database") == 0)
return process_command_load(buf, index, &result);
// {:reload, _}
if (strlen(atom) == 6 && strcmp(atom, "reload") == 0)
return magic_open_all();
// {:stop, _}
if (strlen(atom) == 4 && strcmp(atom, "stop") == 0)
exit(ERROR_OK);
error(&result, "badarg");
}
void process_command_file(byte *buf, int index, ei_x_buff *result) {
int termtype, termsize;
char path[4097];
long bin_length;
if (!magic_loaded)
return error(result, "magic_database_not_loaded");
ei_get_type(buf, &index, &termtype, &termsize);
if (termtype != ERL_BINARY_EXT)
return error(result, "badarg");
if (termsize > 4096)
return error(result, "enametoolong");
EI_ENSURE(ei_decode_binary(buf, &index, path, &bin_length));
path[termsize] = '\0';
process_file(path, result);
}
void process_command_bytes(byte *buf, int index, ei_x_buff *result) {
if (!magic_loaded)
return error(result, "magic_database_not_loaded");
int termtype, termsize;
long bin_length;
char bytes[51];
EI_ENSURE(ei_get_type(buf, &index, &termtype, &termsize));
if (termtype != ERL_BINARY_EXT)
return error(result, "badarg");
if (termsize > 50)
return error(result, "toolong");
EI_ENSURE(ei_decode_binary(buf, &index, bytes, &bin_length));
bytes[termsize] = '\0';
process_bytes(bytes, termsize, result);
}
void process_command_load(byte *buf, int index, ei_x_buff *result) {
char path[4097];
int termtype, termsize;
ei_get_type(buf, &index, &termtype, &termsize);
if (termtype == ERL_BINARY_EXT) {
if (termsize > 4096)
return error(result, "enametoolong");
long bin_length;
EI_ENSURE(ei_decode_binary(buf, &index, path, &bin_length));
path[termsize] = '\0';
return process_load(result, path);
}
if (termtype == ERL_ATOM_EXT) {
EI_ENSURE(ei_decode_atom(buf, &index, path));
if (strlen(path) == 7 && strcmp(path, "default") == 0)
return process_load(result, NULL);
}
error(result, "badarg");
}
void process_load(ei_x_buff *result, char *path) {
if (magic_load_all(path) == 0) {
EI_ENSURE(ei_x_encode_atom(result, "ok"));
EI_ENSURE(ei_x_encode_atom(result, "loaded"));
} else {
EI_ENSURE(ei_x_encode_atom(result, "error"));
EI_ENSURE(ei_x_encode_atom(result, "not_loaded"));
}
send_and_free(result);
}
void setup_environment() { opterr = 0; }
void magic_close_all() {
magic_loaded = false;
if (magic_mime_encoding) {
magic_close(magic_mime_encoding);
magic_mime_encoding = NULL;
}
if (magic_mime_type) {
magic_close(magic_mime_type);
magic_mime_type = NULL;
}
if (magic_type_name) {
magic_close(magic_type_name);
magic_type_name = NULL;
}
}
void magic_open_all() {
magic_close_all();
magic_mime_encoding = magic_open(MAGIC_FLAGS_COMMON | MAGIC_MIME_ENCODING);
magic_mime_type = magic_open(MAGIC_FLAGS_COMMON | MAGIC_MIME_TYPE);
magic_type_name = magic_open(MAGIC_FLAGS_COMMON | MAGIC_NONE);
if (magic_mime_encoding && magic_mime_type && magic_type_name) {
ei_x_buff ok_buf;
EI_ENSURE(ei_x_new_with_version(&ok_buf));
EI_ENSURE(ei_x_encode_atom(&ok_buf, "ready"));
return send_and_free(&ok_buf);
}
exit(ERROR_MAGIC);
}
int magic_load_all(char *path) {
int res;
if ((res = magic_load(magic_mime_encoding, path)) != 0)
return res;
if ((res = magic_load(magic_mime_type, path)) != 0)
return res;
if ((res = magic_load(magic_type_name, path)) != 0)
return res;
magic_loaded = true;
return 0;
}
void process_bytes(char *path, int size, ei_x_buff *result) {
const char *mime_type_result = magic_buffer(magic_mime_type, path, size);
const int mime_type_errno = magic_errno(magic_mime_type);
if (mime_type_errno > 0)
return handle_magic_error(magic_mime_type, mime_type_errno, result);
const char *mime_encoding_result =
magic_buffer(magic_mime_encoding, path, size);
int mime_encoding_errno = magic_errno(magic_mime_encoding);
if (mime_encoding_errno > 0)
return handle_magic_error(magic_mime_encoding, mime_encoding_errno, result);
const char *type_name_result = magic_buffer(magic_type_name, path, size);
int type_name_errno = magic_errno(magic_type_name);
if (type_name_errno > 0)
return handle_magic_error(magic_type_name, type_name_errno, result);
EI_ENSURE(ei_x_encode_atom(result, "ok"));
EI_ENSURE(ei_x_encode_tuple_header(result, 3));
EI_ENSURE(
ei_x_encode_binary(result, mime_type_result, strlen(mime_type_result)));
EI_ENSURE(ei_x_encode_binary(result, mime_encoding_result,
strlen(mime_encoding_result)));
EI_ENSURE(
ei_x_encode_binary(result, type_name_result, strlen(type_name_result)));
send_and_free(result);
}
void handle_magic_error(magic_t handle, int errn, ei_x_buff *result) {
const char *error = magic_error(handle);
EI_ENSURE(ei_x_encode_atom(result, "error"));
EI_ENSURE(ei_x_encode_tuple_header(result, 2));
long errlon = (long)errn;
EI_ENSURE(ei_x_encode_long(result, errlon));
EI_ENSURE(ei_x_encode_binary(result, error, strlen(error)));
send_and_free(result);
}
void process_file(char *path, ei_x_buff *result) {
const char *mime_type_result = magic_file(magic_mime_type, path);
const int mime_type_errno = magic_errno(magic_mime_type);
if (mime_type_errno > 0)
return handle_magic_error(magic_mime_type, mime_type_errno, result);
const char *mime_encoding_result = magic_file(magic_mime_encoding, path);
int mime_encoding_errno = magic_errno(magic_mime_encoding);
if (mime_encoding_errno > 0)
return handle_magic_error(magic_mime_encoding, mime_encoding_errno, result);
const char *type_name_result = magic_file(magic_type_name, path);
int type_name_errno = magic_errno(magic_type_name);
if (type_name_errno > 0)
return handle_magic_error(magic_type_name, type_name_errno, result);
EI_ENSURE(ei_x_encode_atom(result, "ok"));
EI_ENSURE(ei_x_encode_tuple_header(result, 3));
EI_ENSURE(
ei_x_encode_binary(result, mime_type_result, strlen(mime_type_result)));
EI_ENSURE(ei_x_encode_binary(result, mime_encoding_result,
strlen(mime_encoding_result)));
EI_ENSURE(
ei_x_encode_binary(result, type_name_result, strlen(type_name_result)));
send_and_free(result);
}
// Adapted from https://erlang.org/doc/tutorial/erl_interface.html
// Changed `read_cmd`, the original one was buggy given some length (due to
// endinaness).
// TODO: Check if `write_cmd` exhibits the same issue.
size_t read_exact(byte *buf, size_t len) {
- int i, got = 0;
+ int i;
+ size_t got = 0;
do {
if ((i = read(0, buf + got, len - got)) <= 0) {
return (i);
}
got += i;
} while (got < len);
return (len);
}
size_t write_exact(byte *buf, size_t len) {
- int i, wrote = 0;
+ int i;
+ size_t wrote = 0;
do {
if ((i = write(1, buf + wrote, len - wrote)) <= 0)
return (i);
wrote += i;
} while (wrote < len);
return (len);
}
size_t read_cmd(byte *buf) {
int i;
if ((i = read(0, buf, sizeof(uint16_t))) <= 0) {
return (i);
}
uint16_t len16 = *(uint16_t *)buf;
len16 = ntohs(len16);
// Buffer isn't large enough: just return possible len, without reading.
// Up to the caller of verifying the size again and return an error.
// buf left unchanged, stdin emptied of X bytes.
if (len16 > COMMAND_LEN) {
fdseek(len16);
return len16;
}
return read_exact(buf, len16);
}
size_t write_cmd(byte *buf, size_t len) {
byte li;
li = (len >> 8) & 0xff;
write_exact(&li, 1);
li = len & 0xff;
write_exact(&li, 1);
return write_exact(buf, len);
}
void send_and_free(ei_x_buff *result) {
write_cmd(result->buff, result->index);
EI_ENSURE(ei_x_free(result));
}
void error(ei_x_buff *result, const char *error) {
EI_ENSURE(ei_x_encode_atom(result, "error"));
EI_ENSURE(ei_x_encode_atom(result, error));
send_and_free(result);
}
void fdseek(uint16_t count) {
int i = 0;
while (i < count) {
getchar();
i += 1;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 24, 12:53 AM (1 d, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39235
Default Alt Text
(29 KB)
Attached To
Mode
R20 majic
Attached
Detach File
Event Timeline
Log In to Comment