Skip to content
Snippets Groups Projects
Commit 0a444f2e authored by href's avatar href
Browse files

Merge branch 'ei' into develop

parents 02776a3f 8dab8223
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,9 @@ defmodule GenMagic.Helpers do ...@@ -5,7 +5,9 @@ defmodule GenMagic.Helpers do
alias GenMagic.Result alias GenMagic.Result
alias GenMagic.Server alias GenMagic.Server
@spec perform_once(Path.t(), [Server.option()]) :: {:ok, Result.t()} | {:error, term()}
@spec perform_once(Path.t() | {:bytes, binary}, [Server.option()]) ::
{:ok, Result.t()} | {:error, term()}
@doc """ @doc """
Runs a one-shot process without supervision. Runs a one-shot process without supervision.
......
...@@ -81,7 +81,8 @@ defmodule GenMagic.Server do ...@@ -81,7 +81,8 @@ defmodule GenMagic.Server do
@spec child_spec([option()]) :: Supervisor.child_spec() @spec child_spec([option()]) :: Supervisor.child_spec()
@spec start_link([option()]) :: :gen_statem.start_ret() @spec start_link([option()]) :: :gen_statem.start_ret()
@spec perform(t(), Path.t(), timeout()) :: {:ok, Result.t()} | {:error, term() | String.t()} @spec perform(t(), Path.t() | {:bytes, binary()}, timeout()) ::
{:ok, Result.t()} | {:error, term() | String.t()}
@spec status(t(), timeout()) :: {:ok, Status.t()} | {:error, term()} @spec status(t(), timeout()) :: {:ok, Status.t()} | {:error, term()}
@spec stop(t(), term(), timeout()) :: :ok @spec stop(t(), term(), timeout()) :: :ok
...@@ -185,10 +186,9 @@ defmodule GenMagic.Server do ...@@ -185,10 +186,9 @@ defmodule GenMagic.Server do
end end
@doc false @doc false
def starting(:info, {port, {:data, binary}}, %{port: port} = data) do def starting(:info, {port, {:data, ready}}, %{port: port} = data) do
case :erlang.binary_to_term(binary) do case :erlang.binary_to_term(ready) do
:ready -> :ready -> {:next_state, :available, data}
{:next_state, :available, data}
end end
end end
...@@ -198,6 +198,7 @@ defmodule GenMagic.Server do ...@@ -198,6 +198,7 @@ defmodule GenMagic.Server do
1 -> :no_database 1 -> :no_database
2 -> :no_argument 2 -> :no_argument
3 -> :missing_database 3 -> :missing_database
code -> {:unexpected_error, code}
end end
{:stop, {:error, error}, data} {:stop, {:error, error}, data}
...@@ -243,12 +244,10 @@ defmodule GenMagic.Server do ...@@ -243,12 +244,10 @@ defmodule GenMagic.Server do
end end
@doc false @doc false
def processing(:info, {port, {:data, response}}, %{port: port} = data) do def processing(:info, {port, {:data, response}}, %{port: port, request: {_, from, _}} = data) do
{_, from, _} = data.request
data = %{data | request: nil}
response = {:reply, from, handle_response(response)} response = {:reply, from, handle_response(response)}
next_state = (data.cycles >= data.recycle_threshold && :recycling) || :available next_state = (data.cycles >= data.recycle_threshold && :recycling) || :available
{:next_state, next_state, data, [response, :hibernate]} {:next_state, next_state, %{data | request: nil}, [response, :hibernate]}
end end
@doc false @doc false
...@@ -279,11 +278,10 @@ defmodule GenMagic.Server do ...@@ -279,11 +278,10 @@ defmodule GenMagic.Server do
@errnos %{ @errnos %{
2 => :enoent, 2 => :enoent,
13 => :eaccess, 13 => :eaccess,
21 => :eisdir,
20 => :enotdir, 20 => :enotdir,
12 => :enomem, 12 => :enomem,
24 => :emfile, 24 => :emfile,
36 => :enametoolong, 36 => :enametoolong
} }
@errno Map.keys(@errnos) @errno Map.keys(@errnos)
...@@ -292,6 +290,7 @@ defmodule GenMagic.Server do ...@@ -292,6 +290,7 @@ defmodule GenMagic.Server do
{:ok, {mime_type, encoding, content}} -> {:ok, Result.build(mime_type, encoding, content)} {:ok, {mime_type, encoding, content}} -> {:ok, Result.build(mime_type, encoding, content)}
{:error, {errno, _}} when errno in @errno -> {:error, @errnos[errno]} {:error, {errno, _}} when errno in @errno -> {:error, @errnos[errno]}
{:error, {errno, string}} -> {:error, "#{errno}: #{string}"} {:error, {errno, string}} -> {:error, "#{errno}: #{string}"}
{:error, _} = error -> error
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// The Sorcerer’s Apprentice // The Sorcerer’s Apprentice
// //
// To use this program, compile it with dynamically linked libmagic, as mirrored // To use this program, compile it with dynamically linked libmagic, as mirrored
// at https://github.com/threatstack/libmagic. You may install it with apt-get, // at https://github.com/file/file. You may install it with apt-get,
// yum or brew. Refer to the Makefile for further reference. // yum or brew. Refer to the Makefile for further reference.
// //
// This program is designed to run interactively as a backend daemon to the // This program is designed to run interactively as a backend daemon to the
...@@ -12,18 +12,34 @@ ...@@ -12,18 +12,34 @@
// //
// Where each argument either refers to a compiled or uncompiled magic database, // Where each argument either refers to a compiled or uncompiled magic database,
// or the default database. They will be loaded in the sequence that they were // or the default database. They will be loaded in the sequence that they were
// specified. Note that you must specify at least one database. Erlang Term // specified. Note that you must specify at least one database.
// //
// -- main: send atom ready // Communication is done over STDIN/STDOUT as binary packets of 2 bytes length
// enter loop // 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.
// //
// -- while // Once the program is ready, it sends the `:ready` atom. The startup can fail
// get {:file, path} -> process_file -> ok | error // for multiples reasons, and the program will exit accordingly:
// {:bytes, path} -> process_bytes -> ok | error // - 1: No database
// ok: {:ok, {type, encoding, name}} // - 2: Missing/Bad argument
// error: {:error, :badarg} | {:error, {errno, String.t()}} // - 3: Missing database
// {:stop, _} -> exit(ERROR_OK) -> exit 0
// //
// Commands are sent to the program STDIN as an erlang term of `{Operation,
// Argument}`, and response of `{:ok | :error, Response}`.
//
// Invalid packets will cause the program to exit (exit code 4). This will
// happen if your Erlang Term format doesn't match the version the program has
// been compiled with, or if you send a command too huge.
//
// The program may exit with error codes 5 or 255 if something went wrong (such
// as error allocating terms, or if stdin is lost).
//
// Commands:
// {: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 <ei.h> #include <ei.h>
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
...@@ -36,8 +52,6 @@ ...@@ -36,8 +52,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#define USAGE "[--database-file <path/to/magic.mgc> | --database-default, ...]"
#define DELIMITER "\t"
#define ERROR_OK 0 #define ERROR_OK 0
#define ERROR_NO_DATABASE 1 #define ERROR_NO_DATABASE 1
...@@ -46,11 +60,10 @@ ...@@ -46,11 +60,10 @@
#define ERROR_BAD_TERM 4 #define ERROR_BAD_TERM 4
#define ERROR_EI 5 #define ERROR_EI 5
#define ANSI_INFO "\x1b[37m" // gray // We use a bigger than possible valid command length (around 4111 bytes) to
#define ANSI_OK "\x1b[32m" // green // allow more precise errors when using too long paths.
#define ANSI_ERROR "\x1b[31m" // red #define COMMAND_LEN 8000
#define ANSI_IGNORE "\x1b[90m" // red #define COMMAND_BUFFER_SIZE COMMAND_LEN + 1
#define ANSI_RESET "\x1b[0m"
#define MAGIC_FLAGS_COMMON (MAGIC_CHECK | MAGIC_ERROR) #define MAGIC_FLAGS_COMMON (MAGIC_CHECK | MAGIC_ERROR)
magic_t magic_setup(int flags); magic_t magic_setup(int flags);
...@@ -62,14 +75,14 @@ void setup_options(int argc, char **argv); ...@@ -62,14 +75,14 @@ void setup_options(int argc, char **argv);
void setup_options_file(char *optarg); void setup_options_file(char *optarg);
void setup_options_default(); void setup_options_default();
void setup_system(); void setup_system();
int process_command(byte *buf); int process_command(uint16_t len, byte *buf);
void process_line(char *line);
void process_file(char *path, 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_bytes(char *bytes, int size, ei_x_buff *result);
size_t read_cmd(byte *buf); size_t read_cmd(byte *buf);
size_t write_cmd(byte *buf, size_t len); size_t write_cmd(byte *buf, size_t len);
void error(ei_x_buff *result, const char *error); void error(ei_x_buff *result, const char *error);
void handle_magic_error(magic_t handle, int errn, ei_x_buff *result); void handle_magic_error(magic_t handle, int errn, ei_x_buff *result);
void fdseek(uint16_t count);
struct magic_file { struct magic_file {
struct magic_file *prev; struct magic_file *prev;
...@@ -95,29 +108,35 @@ int main(int argc, char **argv) { ...@@ -95,29 +108,35 @@ int main(int argc, char **argv) {
if (ei_x_free(&ok_buf) != 0) if (ei_x_free(&ok_buf) != 0)
exit(ERROR_EI); exit(ERROR_EI);
byte buf[4112]; byte buf[COMMAND_BUFFER_SIZE];
while (read_cmd(buf) > 0) { uint16_t len;
process_command(buf); while ((len = read_cmd(buf)) > 0) {
process_command(len, buf);
} }
return 255; return 255;
} }
int process_command(byte *buf) { int process_command(uint16_t len, byte *buf) {
ei_x_buff result; ei_x_buff result;
char atom[128]; char atom[128];
int index, version, arity, termtype, termsize; int index, version, arity, termtype, termsize;
index = 0; index = 0;
if (ei_decode_version(buf, &index, &version) != 0) {
exit(ERROR_BAD_TERM);
}
// Initialize result // Initialize result
if (ei_x_new_with_version(&result) || ei_x_encode_tuple_header(&result, 2)) { if (ei_x_new_with_version(&result) || ei_x_encode_tuple_header(&result, 2)) {
exit(ERROR_EI); exit(ERROR_EI);
} }
if (len >= COMMAND_LEN) {
error(&result, "badarg");
return 1;
}
if (ei_decode_version(buf, &index, &version) != 0) {
exit(ERROR_BAD_TERM);
}
if (ei_decode_tuple_header(buf, &index, &arity) != 0) { if (ei_decode_tuple_header(buf, &index, &arity) != 0) {
error(&result, "badarg"); error(&result, "badarg");
return 1; return 1;
...@@ -137,11 +156,16 @@ int process_command(byte *buf) { ...@@ -137,11 +156,16 @@ int process_command(byte *buf) {
char path[4097]; char path[4097];
ei_get_type(buf, &index, &termtype, &termsize); ei_get_type(buf, &index, &termtype, &termsize);
if (termtype == ERL_BINARY_EXT && termsize < 4096) { if (termtype == ERL_BINARY_EXT) {
long bin_length; if (termsize < 4096) {
ei_decode_binary(buf, &index, path, &bin_length); long bin_length;
path[termsize] = '\0'; ei_decode_binary(buf, &index, path, &bin_length);
process_file(path, &result); path[termsize] = '\0';
process_file(path, &result);
} else {
error(&result, "enametoolong");
return 1;
}
} else { } else {
error(&result, "badarg"); error(&result, "badarg");
return 1; return 1;
...@@ -378,9 +402,15 @@ size_t read_cmd(byte *buf) { ...@@ -378,9 +402,15 @@ size_t read_cmd(byte *buf) {
} }
uint16_t len16 = *(uint16_t *)buf; uint16_t len16 = *(uint16_t *)buf;
len16 = ntohs(len16); len16 = ntohs(len16);
if (len16 > 4111) {
exit(ERROR_BAD_TERM); // 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); return read_exact(buf, len16);
} }
...@@ -404,3 +434,11 @@ void error(ei_x_buff *result, const char *error) { ...@@ -404,3 +434,11 @@ void error(ei_x_buff *result, const char *error) {
if (ei_x_free(result) != 0) if (ei_x_free(result) != 0)
exit(ERROR_EI); exit(ERROR_EI);
} }
void fdseek(uint16_t count) {
int i = 0;
while (i < count) {
getchar();
i += 1;
}
}
defmodule GenMagic.ApprenticeTest do defmodule GenMagic.ApprenticeTest do
use GenMagic.MagicCase use GenMagic.MagicCase
@tmp_path "/tmp/testgenmagicx"
test "sends ready" do test "sends ready" do
port = Port.open(GenMagic.Config.get_port_name(), GenMagic.Config.get_port_options([])) port = Port.open(GenMagic.Config.get_port_name(), GenMagic.Config.get_port_options([]))
assert_ready(port) assert_ready(port)
...@@ -85,19 +87,39 @@ defmodule GenMagic.ApprenticeTest do ...@@ -85,19 +87,39 @@ defmodule GenMagic.ApprenticeTest do
end end
test "works with big file path", %{port: port} do test "works with big file path", %{port: port} do
file = too_big() <> "/a" # Test with longest valid path.
File.mkdir_p!(too_big()) {dir, bigfile} = too_big(@tmp_path, "/a")
File.touch!(file) File.mkdir_p!(dir)
on_exit(fn -> File.rm_rf!("/tmp/testmagicex/") end) File.touch!(bigfile)
send(port, {self(), {:command, :erlang.term_to_binary({:file, file})}}) on_exit(fn -> File.rm_rf!(@tmp_path) end)
send(port, {self(), {:command, :erlang.term_to_binary({:file, bigfile})}})
assert_receive {^port, {:data, data}} assert_receive {^port, {:data, data}}
assert {:ok, _} = :erlang.binary_to_term(data) assert {:ok, _} = :erlang.binary_to_term(data)
refute_receive _ refute_receive _
file = too_big() <> "/aaaaaaaaaa"
# This path should be long enough for buffers, but larger than a valid path name. Magic will return an errno 36.
file = @tmp_path <> String.duplicate("a", 256)
send(port, {self(), {:command, :erlang.term_to_binary({:file, file})}})
assert_receive {^port, {:data, data}}
assert {:error, {36, _}} = :erlang.binary_to_term(data)
refute_receive _
# Theses filename should be too big for the path buffer.
file = bigfile <> "aaaaaaaaaa"
send(port, {self(), {:command, :erlang.term_to_binary({:file, file})}})
assert_receive {^port, {:data, data}}
assert {:error, :enametoolong} = :erlang.binary_to_term(data)
refute_receive _
# This call should be larger than the COMMAND_BUFFER_SIZE. Ensure nothing bad happens!
file = String.duplicate(bigfile, 4)
send(port, {self(), {:command, :erlang.term_to_binary({:file, file})}}) send(port, {self(), {:command, :erlang.term_to_binary({:file, file})}})
assert_receive {^port, {:data, data}} assert_receive {^port, {:data, data}}
assert {:error, :badarg} = :erlang.binary_to_term(data) assert {:error, :badarg} = :erlang.binary_to_term(data)
refute_receive _ refute_receive _
# We re-run a valid call to ensure the buffer/... haven't been corrupted in port land.
send(port, {self(), {:command, :erlang.term_to_binary({:file, bigfile})}})
assert_receive {^port, {:data, data}}
assert {:ok, _} = :erlang.binary_to_term(data)
refute_receive _
end end
end end
...@@ -106,7 +128,20 @@ defmodule GenMagic.ApprenticeTest do ...@@ -106,7 +128,20 @@ defmodule GenMagic.ApprenticeTest do
assert :ready == :erlang.binary_to_term(data) assert :ready == :erlang.binary_to_term(data)
end end
def too_big do def too_big(path, filename, limit \\ 4095) do
"/tmp/testmagicex/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" last_len = byte_size(filename)
path_len = byte_size(path)
needed = limit - (last_len + path_len)
extra = make_too_big(needed, "")
{path <> extra, path <> extra <> filename}
end
def make_too_big(needed, acc) when needed <= 255 do
acc <> "/" <> String.duplicate("a", needed - 1)
end
def make_too_big(needed, acc) do
acc = acc <> "/" <> String.duplicate("a", 254)
make_too_big(needed - 255, acc)
end end
end end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment