fix: skip chmod on pre-existing directories in SaveUploadedFile

Only apply os.Chmod to directories newly created by os.MkdirAll.
Previously, chmod was called unconditionally, which broke saving
files into pre-existing directories not owned by the process
(e.g., /tmp) with "operation not permitted" error.

Refs #4622

Agent-Logs-Url: https://github.com/odlev/gin/sessions/2d0f57ad-a46e-45f6-a2f2-b6d4c352f22e

Co-authored-by: odlev <65655276+odlev@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-04-07 20:37:04 +00:00 committed by GitHub
parent d3ffc99852
commit 592a2d08c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 2 deletions

View File

@ -728,11 +728,16 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm
mode = perm[0]
}
dir := filepath.Dir(dst)
_, statErr := os.Stat(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 to avoid "operation not permitted"
// errors on pre-existing directories we may not own (e.g., /tmp).
if statErr != nil {
if err = os.Chmod(dir, mode); err != nil {
return err
}
}
out, err := os.Create(dst)

View File

@ -274,6 +274,31 @@ func TestSaveUploadedFileWithPermissionFailed(t *testing.T) {
require.Error(t, c.SaveUploadedFile(f, "test/permission_test", mode))
}
func TestSaveUploadedFileToExistingDir(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
w, err := mw.CreateFormFile("file", "test")
require.NoError(t, err)
_, err = w.Write([]byte("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)
// Save to a pre-existing directory. Previously this would fail with
// "chmod <dir>: operation not permitted" when the directory was not
// owned by the current process (e.g., /tmp).
dir := t.TempDir()
dst := filepath.Join(dir, "uploaded.txt")
require.NoError(t, c.SaveUploadedFile(f, dst))
t.Cleanup(func() {
os.Remove(dst)
})
}
func TestContextReset(t *testing.T) {
router := New()
c := router.allocateContext(0)