summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/media/ffmpeg.go88
-rw-r--r--internal/media/util.go47
2 files changed, 104 insertions, 31 deletions
diff --git a/internal/media/ffmpeg.go b/internal/media/ffmpeg.go
index 0443f95b8..58c2f9503 100644
--- a/internal/media/ffmpeg.go
+++ b/internal/media/ffmpeg.go
@@ -21,6 +21,7 @@ import (
"context"
"encoding/json"
"errors"
+ "os"
"path"
"strconv"
"strings"
@@ -39,10 +40,7 @@ import (
// any metadata encoded into the media stream itself will not be cleared. This is the best we
// can do without absolutely tanking performance by requiring transcodes :(
func ffmpegClearMetadata(ctx context.Context, outpath, inpath string) error {
- // Get directory from filepath.
- dirpath := path.Dir(inpath)
-
- return ffmpeg(ctx, dirpath,
+ return ffmpeg(ctx, inpath, outpath,
// Only log errors.
"-loglevel", "error",
@@ -66,18 +64,15 @@ func ffmpegClearMetadata(ctx context.Context, outpath, inpath string) error {
}
// ffmpegGenerateWebpThumb generates a thumbnail webp from input media of any type, useful for any media.
-func ffmpegGenerateWebpThumb(ctx context.Context, filepath, outpath string, width, height int, pixfmt string) error {
- // Get directory from filepath.
- dirpath := path.Dir(filepath)
-
+func ffmpegGenerateWebpThumb(ctx context.Context, inpath, outpath string, width, height int, pixfmt string) error {
// Generate thumb with ffmpeg.
- return ffmpeg(ctx, dirpath,
+ return ffmpeg(ctx, inpath, outpath,
// Only log errors.
"-loglevel", "error",
// Input file.
- "-i", filepath,
+ "-i", inpath,
// Encode using libwebp.
// (NOT as libwebp_anim).
@@ -116,27 +111,24 @@ func ffmpegGenerateWebpThumb(ctx context.Context, filepath, outpath string, widt
}
// ffmpegGenerateStatic generates a static png from input image of any type, useful for emoji.
-func ffmpegGenerateStatic(ctx context.Context, filepath string) (string, error) {
+func ffmpegGenerateStatic(ctx context.Context, inpath string) (string, error) {
var outpath string
// Generate thumb output path REPLACING extension.
- if i := strings.IndexByte(filepath, '.'); i != -1 {
- outpath = filepath[:i] + "_static.png"
+ if i := strings.IndexByte(inpath, '.'); i != -1 {
+ outpath = inpath[:i] + "_static.png"
} else {
return "", gtserror.New("input file missing extension")
}
- // Get directory from filepath.
- dirpath := path.Dir(filepath)
-
// Generate static with ffmpeg.
- if err := ffmpeg(ctx, dirpath,
+ if err := ffmpeg(ctx, inpath, outpath,
// Only log errors.
"-loglevel", "error",
// Input file.
- "-i", filepath,
+ "-i", inpath,
// Only first frame.
"-frames:v", "1",
@@ -157,18 +149,45 @@ func ffmpegGenerateStatic(ctx context.Context, filepath string) (string, error)
return outpath, nil
}
-// ffmpeg calls `ffmpeg [args...]` (WASM) with directory path mounted in runtime.
-func ffmpeg(ctx context.Context, dirpath string, args ...string) error {
+// ffmpeg calls `ffmpeg [args...]` (WASM) with in + out paths mounted in runtime.
+func ffmpeg(ctx context.Context, inpath string, outpath string, args ...string) error {
var stderr byteutil.Buffer
rc, err := _ffmpeg.Ffmpeg(ctx, _ffmpeg.Args{
Stderr: &stderr,
Args: args,
Config: func(modcfg wazero.ModuleConfig) wazero.ModuleConfig {
- fscfg := wazero.NewFSConfig() // needs /dev/urandom
- fscfg = fscfg.WithReadOnlyDirMount("/dev", "/dev")
- fscfg = fscfg.WithDirMount(dirpath, dirpath)
- modcfg = modcfg.WithFSConfig(fscfg)
- return modcfg
+ fscfg := wazero.NewFSConfig()
+
+ // Needs read-only access to
+ // /dev/urandom for some types.
+ urandom := &allowFiles{
+ {
+ abs: "/dev/urandom",
+ flag: os.O_RDONLY,
+ perm: 0,
+ },
+ }
+ fscfg = fscfg.WithFSMount(urandom, "/dev")
+
+ // In+out dirs are always the same (tmp),
+ // so we can share one file system for
+ // both + grant different perms to inpath
+ // (read only) and outpath (read+write).
+ shared := &allowFiles{
+ {
+ abs: inpath,
+ flag: os.O_RDONLY,
+ perm: 0,
+ },
+ {
+ abs: outpath,
+ flag: os.O_RDWR | os.O_CREATE | os.O_TRUNC,
+ perm: 0666,
+ },
+ }
+ fscfg = fscfg.WithFSMount(shared, path.Dir(inpath))
+
+ return modcfg.WithFSConfig(fscfg)
},
})
if err != nil {
@@ -183,9 +202,6 @@ func ffmpeg(ctx context.Context, dirpath string, args ...string) error {
func ffprobe(ctx context.Context, filepath string) (*result, error) {
var stdout byteutil.Buffer
- // Get directory from filepath.
- dirpath := path.Dir(filepath)
-
// Run ffprobe on our given file at path.
_, err := _ffmpeg.Ffprobe(ctx, _ffmpeg.Args{
Stdout: &stdout,
@@ -222,9 +238,19 @@ func ffprobe(ctx context.Context, filepath string) (*result, error) {
Config: func(modcfg wazero.ModuleConfig) wazero.ModuleConfig {
fscfg := wazero.NewFSConfig()
- fscfg = fscfg.WithReadOnlyDirMount(dirpath, dirpath)
- modcfg = modcfg.WithFSConfig(fscfg)
- return modcfg
+
+ // Needs read-only access
+ // to file being probed.
+ in := &allowFiles{
+ {
+ abs: filepath,
+ flag: os.O_RDONLY,
+ perm: 0,
+ },
+ }
+ fscfg = fscfg.WithFSMount(in, path.Dir(filepath))
+
+ return modcfg.WithFSConfig(fscfg)
},
})
if err != nil {
diff --git a/internal/media/util.go b/internal/media/util.go
index f743e3821..22121a546 100644
--- a/internal/media/util.go
+++ b/internal/media/util.go
@@ -22,13 +22,60 @@ import (
"errors"
"fmt"
"io"
+ "io/fs"
"os"
+ "path"
"codeberg.org/gruf/go-bytesize"
"codeberg.org/gruf/go-iotools"
"codeberg.org/gruf/go-mimetypes"
)
+// file represents one file
+// with the given flag and perms.
+type file struct {
+ abs string
+ flag int
+ perm os.FileMode
+}
+
+// allowFiles implements fs.FS to allow
+// access to a specified slice of files.
+type allowFiles []file
+
+// Open implements fs.FS.
+func (af allowFiles) Open(name string) (fs.File, error) {
+ for _, file := range af {
+ var (
+ abs = file.abs
+ flag = file.flag
+ perm = file.perm
+ )
+
+ // Allowed to open file
+ // at absolute path.
+ if name == file.abs {
+ return os.OpenFile(abs, flag, perm)
+ }
+
+ // Check for other valid reads.
+ thisDir, thisFile := path.Split(file.abs)
+
+ // Allowed to read directory itself.
+ if name == thisDir || name == "." {
+ return os.OpenFile(thisDir, flag, perm)
+ }
+
+ // Allowed to read file
+ // itself (at relative path).
+ if name == thisFile {
+ return os.OpenFile(abs, flag, perm)
+ }
+ }
+
+ return nil, os.ErrPermission
+}
+
// getExtension splits file extension from path.
func getExtension(path string) string {
for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- {