Merge 2ac84ddd382d4459632fe59f0919551e78e6170e into 5f4f9643258dc2a65e684b63f12c8d543c936c67

This commit is contained in:
TT 2026-05-22 11:35:09 +00:00 committed by GitHub
commit bc90ed46c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 10 deletions

View File

@ -715,6 +715,24 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
return c.Request.MultipartForm, err
}
func dirExists(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
return info.IsDir()
}
func ensureDirWithMode(dir string, mode fs.FileMode) error {
if dirExists(dir) {
return nil
}
if err := os.MkdirAll(dir, mode); err != nil {
return err
}
return os.Chmod(dir, mode)
}
// 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,10 +746,9 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm
mode = perm[0]
}
dir := filepath.Dir(dst)
if err = os.MkdirAll(dir, mode); err != nil {
return err
}
if err = os.Chmod(dir, mode); err != nil {
// Only chmod newly created directories. Attempting to chmod
// pre-existing directories (e.g. /tmp) may fail with EPERM.
if err = ensureDirWithMode(dir, mode); err != nil {
return err
}

View File

@ -20,6 +20,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
@ -247,13 +248,60 @@ 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"))
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, info.Mode().Perm(), mode)
assert.Equal(t, mode, info.Mode().Perm())
}
}
func TestDirExists(t *testing.T) {
tmpDir := t.TempDir()
assert.True(t, dirExists(tmpDir))
assert.False(t, dirExists(filepath.Join(tmpDir, "missing")))
filePath := filepath.Join(tmpDir, "file")
require.NoError(t, os.WriteFile(filePath, []byte("test"), 0o600))
assert.False(t, dirExists(filePath))
}
func TestSaveUploadedFileKeepsExistingDirPermission(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
w, err := mw.CreateFormFile("file", "permission_test")
require.NoError(t, err)
_, err = w.Write([]byte("permission_test"))
require.NoError(t, err)
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.FormFile("file")
require.NoError(t, err)
tmpDir := t.TempDir()
existingDir := filepath.Join(tmpDir, "existing")
require.NoError(t, os.Mkdir(existingDir, 0o700))
if runtime.GOOS != "windows" {
require.NoError(t, os.Chmod(existingDir, 0o700))
}
var mode fs.FileMode = 0o755
dst := filepath.Join(existingDir, "permission_test")
require.NoError(t, c.SaveUploadedFile(f, dst, mode))
saved, err := os.ReadFile(dst)
require.NoError(t, err)
assert.Equal(t, "permission_test", string(saved))
if runtime.GOOS != "windows" {
info, err := os.Stat(existingDir)
require.NoError(t, err)
assert.Equal(t, fs.FileMode(0o700), info.Mode().Perm())
}
}
func TestSaveUploadedFileWithPermissionFailed(t *testing.T) {