commit: 00d536d9e2c6c76d88724e5c75cc82a857519c65
parent bc75bb35fac172c021f6caa3ee8624cbc5325c9c
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Thu, 30 Jan 2025 15:50:50 +0100
backports: Copy mkdir_p TOCTOU fix from elixir PR 14242
See: https://github.com/elixir-lang/elixir/pull/14242
Diffstat:
2 files changed, 74 insertions(+), 0 deletions(-)
diff --git a/changelog.d/toctou-mkdir.fix b/changelog.d/toctou-mkdir.fix
@@ -0,0 +1 @@
+Backport [Elixir PR 14242](https://github.com/elixir-lang/elixir/pull/14242) fixing racy mkdir and lack of error handling of parent directory creation
+\ No newline at end of file
diff --git a/lib/pleroma/backports.ex b/lib/pleroma/backports.ex
@@ -0,0 +1,72 @@
+# Copyright 2012 Plataformatec
+# Copyright 2021 The Elixir Team
+# SPDX-License-Identifier: Apache-2.0
+
+defmodule Pleroma.Backports do
+ import File, only: [dir?: 1]
+
+ # <https://github.com/elixir-lang/elixir/pull/14242>
+ # To be removed when we require Elixir 1.19
+ @doc """
+ Tries to create the directory `path`.
+
+ Missing parent directories are created. Returns `:ok` if successful, or
+ `{:error, reason}` if an error occurs.
+
+ Typical error reasons are:
+
+ * `:eacces` - missing search or write permissions for the parent
+ directories of `path`
+ * `:enospc` - there is no space left on the device
+ * `:enotdir` - a component of `path` is not a directory
+
+ """
+ @spec mkdir_p(Path.t()) :: :ok | {:error, File.posix() | :badarg}
+ def mkdir_p(path) do
+ do_mkdir_p(IO.chardata_to_string(path))
+ end
+
+ defp do_mkdir_p("/") do
+ :ok
+ end
+
+ defp do_mkdir_p(path) do
+ parent = Path.dirname(path)
+
+ if parent == path do
+ :ok
+ else
+ case do_mkdir_p(parent) do
+ :ok ->
+ case :file.make_dir(path) do
+ {:error, :eexist} ->
+ if dir?(path), do: :ok, else: {:error, :enotdir}
+
+ other ->
+ other
+ end
+
+ e ->
+ e
+ end
+ end
+ end
+
+ @doc """
+ Same as `mkdir_p/1`, but raises a `File.Error` exception in case of failure.
+ Otherwise `:ok`.
+ """
+ @spec mkdir_p!(Path.t()) :: :ok
+ def mkdir_p!(path) do
+ case mkdir_p(path) do
+ :ok ->
+ :ok
+
+ {:error, reason} ->
+ raise File.Error,
+ reason: reason,
+ action: "make directory (with -p)",
+ path: IO.chardata_to_string(path)
+ end
+ end
+end