Zip: Fix potential filesystem permission and timing issues #2532

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2022-07-19 20:41:36 +02:00
parent efbe573038
commit 58521190ba
20 changed files with 129 additions and 80 deletions

View file

@ -99,11 +99,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: ALL" >> /etc/sudoers.d/all && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -111,10 +108,12 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# download models and testdata
RUN wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
RUN mkdir /tmp/photoprism && \
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip

View file

@ -51,11 +51,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: /scripts/entrypoint-init.sh" >> /etc/sudoers.d/init && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -63,7 +60,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# define default directory and user
WORKDIR /photoprism

View file

@ -69,11 +69,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: ALL" >> /etc/sudoers.d/all && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -81,10 +78,12 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# download models and testdata
RUN wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
RUN mkdir /tmp/photoprism && \
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip

View file

@ -71,11 +71,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: /scripts/entrypoint-init.sh" >> /etc/sudoers.d/init && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -83,7 +80,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# define default directory and user
WORKDIR /photoprism

View file

@ -110,11 +110,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: ALL" >> /etc/sudoers.d/all && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -122,10 +119,12 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# download models and testdata
RUN wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
RUN mkdir /tmp/photoprism && \
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip

View file

@ -106,11 +106,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: ALL" >> /etc/sudoers.d/all && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -118,10 +115,12 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# download models and testdata
RUN wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
RUN mkdir /tmp/photoprism && \
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip

View file

@ -106,11 +106,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: ALL" >> /etc/sudoers.d/all && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -118,10 +115,12 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# download models and testdata
RUN wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
RUN mkdir /tmp/photoprism && \
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip

View file

@ -108,11 +108,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: ALL" >> /etc/sudoers.d/all && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -120,10 +117,12 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# download models and testdata
RUN wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
RUN mkdir /tmp/photoprism && \
wget "https://dl.photoprism.app/tensorflow/nsfw.zip?${BUILD_TAG}" -O /tmp/photoprism/nsfw.zip && \
wget "https://dl.photoprism.app/tensorflow/nasnet.zip?${BUILD_TAG}" -O /tmp/photoprism/nasnet.zip && \
wget "https://dl.photoprism.app/tensorflow/facenet.zip?${BUILD_TAG}" -O /tmp/photoprism/facenet.zip && \
wget "https://dl.photoprism.app/qa/testdata.zip?${BUILD_TAG}" -O /tmp/photoprism/testdata.zip

View file

@ -164,7 +164,6 @@ services:
- "./originals:/photoprism/originals" # original media files (photos and videos)
- "./import:/photoprism/import" # *optional* base folder from which files can be imported to originals
- "./storage:/photoprism/storage" # *writable* storage folder for cache, database, and sidecar files (never remove)
- "./backup:/var/lib/photoprism" # *optional* storage folder for database backups
## Traefik Reverse Proxy (required)
## see https://docs.photoprism.app/getting-started/proxies/traefik/

View file

@ -104,11 +104,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: /scripts/entrypoint-init.sh" >> /etc/sudoers.d/init && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -116,8 +113,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# define default directory and user
WORKDIR /photoprism

View file

@ -120,11 +120,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: /scripts/entrypoint-init.sh" >> /etc/sudoers.d/init && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -132,7 +129,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# define default directory and user
WORKDIR /photoprism

View file

@ -118,11 +118,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: /scripts/entrypoint-init.sh" >> /etc/sudoers.d/init && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -130,7 +127,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# define default directory and user
WORKDIR /photoprism

View file

@ -119,11 +119,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
echo "ALL ALL=(ALL) NOPASSWD:SETENV: /scripts/entrypoint-init.sh" >> /etc/sudoers.d/init && \
cp /etc/skel/.bashrc /root/.bashrc && \
/scripts/create-users.sh && \
/scripts/cleanup.sh && \
cp /scripts/heif-convert.sh /usr/local/bin/heif-convert && \
install -d -m 0777 -o 1000 -g 1000 \
/var/lib/photoprism \
/tmp/photoprism \
/photoprism/originals \
/photoprism/import \
/photoprism/storage \
@ -131,7 +128,8 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
/photoprism/storage/albums \
/photoprism/storage/backups \
/photoprism/storage/config \
/photoprism/storage/cache
/photoprism/storage/cache && \
/scripts/cleanup.sh
# define default directory and user
WORKDIR /photoprism

View file

@ -19,7 +19,6 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
@ -85,7 +84,7 @@ func CreateZip(router *gin.RouterGroup) {
zipFileName := path.Join(zipPath, zipBaseName)
// Create temp directory.
if err := os.MkdirAll(zipPath, 0700); err != nil {
if err = os.MkdirAll(zipPath, 0700); err != nil {
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
return
}
@ -101,7 +100,9 @@ func CreateZip(router *gin.RouterGroup) {
// Create zip writer.
zipWriter := zip.NewWriter(newZipFile)
defer zipWriter.Close()
defer func(w *zip.Writer) {
logError("zip", w.Close())
}(zipWriter)
var aliases = make(map[string]int)
@ -162,9 +163,9 @@ func DownloadZip(router *gin.RouterGroup) {
c.FileAttachment(zipFileName, zipBaseName)
if err := os.Remove(zipFileName); err != nil {
log.Errorf("download: failed removing %s (%s)", clean.Log(zipFileName), err.Error())
}
defer func(n string) {
logError("zip", os.Remove(n))
}(zipFileName)
})
}

View file

@ -2,6 +2,7 @@ package config
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"os/user"
@ -13,6 +14,7 @@ import (
// binPaths stores known executable paths.
var binPaths = make(map[string]string, 8)
var tempPath = ""
// findExecutable searches binaries by their name.
func findExecutable(configBin, defaultBin string) (binPath string) {
@ -267,19 +269,48 @@ func (c *Config) SidecarWritable() bool {
return !c.ReadOnly() || c.SidecarPathIsAbs()
}
// TempPath returns a temporary directory name for uploads and downloads.
// TempPath returns the cached temporary directory name for uploads and downloads.
func (c *Config) TempPath() string {
if c.options.TempPath != "" {
if c.options.TempPath[0] != '/' {
c.options.TempPath = fs.Abs(c.options.TempPath)
}
} else if dir, err := os.MkdirTemp(os.TempDir(), "photoprism"); err == nil {
c.options.TempPath = dir
} else {
c.options.TempPath = filepath.Join(os.TempDir(), "photoprism")
// Return cached value?
if tempPath == "" {
tempPath = c.tempPath()
}
return c.options.TempPath
return tempPath
}
// tempPath returns the uncached temporary directory name for uploads and downloads.
func (c *Config) tempPath() string {
// Check configured temp path first.
if c.options.TempPath != "" {
if dir := fs.Abs(c.options.TempPath); dir == "" {
// Ignore.
} else if err := os.MkdirAll(dir, os.ModePerm); err != nil {
// Ignore.
} else if fs.PathWritable(dir) {
return dir
}
}
// Find alternative temp path based on storage serial checksum.
if dir := filepath.Join(os.TempDir(), "photoprism_"+c.SerialChecksum()); dir == "" {
// Ignore.
} else if err := os.MkdirAll(dir, os.ModePerm); err != nil {
// Ignore.
} else if fs.PathWritable(dir) {
return dir
}
// Find alternative temp path based on built-in TempDir() function.
if dir, err := ioutil.TempDir(os.TempDir(), "photoprism_"); err != nil || dir == "" {
// Ignore.
} else if err = os.MkdirAll(dir, os.ModePerm); err != nil {
// Ignore.
} else if fs.PathWritable(dir) {
return dir
}
return os.TempDir()
}
// CachePath returns the path for cache files.

View file

@ -43,13 +43,46 @@ func TestConfig_FFmpegBin(t *testing.T) {
func TestConfig_TempPath(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/storage/testdata/temp", c.TempPath())
d0 := c.tempPath()
t.Logf("c.options.TempPath: '%s'", c.options.TempPath)
t.Logf("c.tempPath(): '%s'", d0)
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/storage/testdata/temp", c.tempPath())
c.options.TempPath = ""
if dir := c.TempPath(); dir == "" {
d1 := c.tempPath()
if d1 == "" {
t.Fatal("temp path is empty")
} else if !strings.HasPrefix(dir, "/tmp/photoprism") {
t.Fatalf("unexpected temp path: %s", dir)
}
if !strings.HasPrefix(d1, "/tmp/photoprism_") {
t.Fatalf("unexpected temp path: %s", d1)
}
d2 := c.tempPath()
if d2 == "" {
t.Fatal("temp path is empty")
}
if !strings.HasPrefix(d2, "/tmp/photoprism_") {
t.Fatalf("unexpected temp path: %s", d2)
}
if d1 != d2 {
t.Fatalf("temp paths should match: '%s' <=> '%s'", d1, d2)
} else {
t.Logf("temp paths match: '%s' == '%s'", d1, d2)
}
if d4 := c.TempPath(); d4 != d0 {
t.Fatalf("temp paths should match: '%s' <=> '%s'", d4, d0)
} else {
t.Logf("temp paths match: '%s' == '%s'", d4, d0)
}
}

View file

@ -14,6 +14,9 @@ var OriginalPaths = []string{
"/photoprism/storage/media/originals",
"/photoprism/media/originals",
"/photoprism/originals",
"/srv/photoprism/storage/media/originals",
"/srv/photoprism/media/originals",
"/srv/photoprism/originals",
"/opt/photoprism/storage/media/originals",
"/opt/photoprism/media/originals",
"/opt/photoprism/originals",
@ -77,6 +80,9 @@ var ImportPaths = []string{
"/photoprism/storage/media/import",
"/photoprism/media/import",
"/photoprism/import",
"/srv/photoprism/storage/media/import",
"/srv/photoprism/media/import",
"/srv/photoprism/import",
"/opt/photoprism/storage/media/import",
"/opt/photoprism/media/import",
"/opt/photoprism/import",

View file

@ -96,9 +96,9 @@ func PathWritable(path string) bool {
if f, err := os.Create(tmpName); err != nil {
return false
} else if err := f.Close(); err != nil {
} else if err = f.Close(); err != nil {
return false
} else if err := os.Remove(tmpName); err != nil {
} else if err = os.Remove(tmpName); err != nil {
return false
}

View file

@ -1,7 +1,5 @@
#!/bin/bash
PATH="/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/scripts"
# abort if not executed as root
if [[ $(id -u) != "0" ]]; then
echo "Usage: run ${0##*/} as root" 1>&2