From 9c81701997dfdcbe865a0d4813045eec3de03ad5 Mon Sep 17 00:00:00 2001
From: Cam Saul <cammsaul@gmail.com>
Date: Mon, 11 Feb 2019 12:47:56 -0800
Subject: [PATCH] Fallback to temp dir for unpacking modules if ./plugins is
 unwritable

---
 src/metabase/plugins.clj | 48 ++++++++++++++++++++++++++++++++--------
 1 file changed, 39 insertions(+), 9 deletions(-)

diff --git a/src/metabase/plugins.clj b/src/metabase/plugins.clj
index 156030adb0b..893736cebb3 100644
--- a/src/metabase/plugins.clj
+++ b/src/metabase/plugins.clj
@@ -10,18 +10,48 @@
             [metabase.util :as u]
             [metabase.util.i18n :refer [trs]]
             [yaml.core :as yaml])
-  (:import java.nio.file.Path))
+  (:import java.io.File
+           [java.nio.file Files Path]))
 
 (defn- plugins-dir-filename ^String []
   (or (env/env :mb-plugins-dir)
-      (str (System/getProperty "user.dir") "/plugins")))
-
-(defn- ^Path plugins-dir
-  "Get a `Path` to the Metabase plugins directory, creating it if needed."
-  []
-  (let [path (files/get-path (plugins-dir-filename))]
-    (files/create-dir-if-not-exists! path)
-    path))
+      (str (System/getProperty "user.dir")
+           File/separator
+           "plugins")))
+
+;; logic for determining plugins dir -- see below
+(defonce ^:private plugins-dir*
+  (delay
+   (let [filename (plugins-dir-filename)]
+     (try
+       ;; attempt to create <current-dir>/plugins if it doesn't already exist. Check that the directory is readable.
+       (u/prog1 (files/get-path filename)
+         (files/create-dir-if-not-exists! <>)
+         (assert (Files/isWritable <>)
+           (str (trs "Metabase does not have permissions to write to plugins directory {0}" filename))))
+       ;; If we couldn't create the directory, or the directory is not writable, fall back to a temporary directory
+       ;; rather than failing to launch entirely. Log instructions for what should be done to fix the problem.
+       (catch Throwable e
+         (log/warn
+          e
+          (trs "Metabase cannot use the plugins directory {0}" filename)
+          "\n"
+          (trs "Please make sure the directory exists and that Metabase has permission to write to it.")
+          (trs "You can change the directory Metabase uses for modules by setting the environment variable MB_PLUGINS_DIR.")
+          (trs "Falling back to a temporary directory for now."))
+         ;; Check whether the fallback temporary directory is writable. If it's not, there's no way for us to
+         ;; gracefully proceed here. Throw an Exception detailing the critical issues.
+         (u/prog1 (files/get-path (System/getProperty "java.io.tmpdir"))
+           (assert (Files/isWritable <>)
+             (str (trs "Metabase cannot write to temporary directory. Please set MB_PLUGINS_DIR to a writable directory and restart Metabase.")))))))))
+
+;; Actual logic is wrapped in a delay rather than a normal function so we don't log the error messages more than once
+;; in cases where we have to fall back to the system temporary directory
+(defn- plugins-dir
+  "Get a `Path` to the Metabase plugins directory, creating it if needed. If it cannot be created for one reason or
+  another, or if we do not have write permissions for it, use a temporary directory instead."
+  ^Path []
+  @plugins-dir*)
 
 (defn- extract-system-modules! []
   (when (io/resource "modules")
-- 
GitLab