From 6cfac86ae77db5f4869baec0e0a7a7ef0852c8b6 Mon Sep 17 00:00:00 2001 From: tengtian Date: Wed, 15 Apr 2026 20:02:50 +0200 Subject: [PATCH] fix: skip chmod on existing directories in SaveUploadedFile SaveUploadedFile called os.Chmod on the target directory even if it already existed. This breaks when saving to system directories like /tmp where the process lacks permission to chmod. Now only newly created directories get chmod'd. Existing directories are left as-is, which is the correct behavior since the caller should not modify permissions of directories they don't own. Fixes #4622 --- context.go | 14 ++++++++++++-- context_test.go | 17 ++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index 5174033e..c0dbbef2 100644 --- a/context.go +++ b/context.go @@ -715,6 +715,11 @@ func (c *Context) MultipartForm() (*multipart.Form, error) { return c.Request.MultipartForm, err } +func dirExists(path string) bool { + info, err := os.Stat(path) + return err == nil && info.IsDir() +} + // SaveUploadedFile uploads the form file to specific dst. func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm ...fs.FileMode) error { src, err := file.Open() @@ -728,11 +733,16 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm mode = perm[0] } dir := filepath.Dir(dst) + dirExisted := dirExists(dir) if err = os.MkdirAll(dir, mode); err != nil { return err } - if err = os.Chmod(dir, mode); err != nil { - return err + // Only chmod newly created directories. Attempting to chmod + // pre-existing directories (e.g. /tmp) may fail with EPERM. + if !dirExisted { + if err = os.Chmod(dir, mode); err != nil { + return err + } } out, err := os.Create(dst) diff --git a/context_test.go b/context_test.go index ef60379d..de2b4140 100644 --- a/context_test.go +++ b/context_test.go @@ -20,6 +20,7 @@ import ( "os" "path/filepath" "reflect" + "runtime" "strconv" "strings" "sync" @@ -247,13 +248,15 @@ func TestSaveUploadedFileWithPermission(t *testing.T) { require.NoError(t, err) assert.Equal(t, "permission_test", f.Filename) var mode fs.FileMode = 0o755 - require.NoError(t, c.SaveUploadedFile(f, "permission_test", mode)) - t.Cleanup(func() { - assert.NoError(t, os.Remove("permission_test")) - }) - info, err := os.Stat(filepath.Dir("permission_test")) - require.NoError(t, err) - assert.Equal(t, info.Mode().Perm(), mode) + tmpDir := t.TempDir() + newSubDir := filepath.Join(tmpDir, "newdir") + dst := filepath.Join(newSubDir, "permission_test") + require.NoError(t, c.SaveUploadedFile(f, dst, mode)) + if runtime.GOOS != "windows" { + info, err := os.Stat(newSubDir) + require.NoError(t, err) + assert.Equal(t, mode, info.Mode().Perm()) + } } func TestSaveUploadedFileWithPermissionFailed(t *testing.T) {