diff --git a/modules/storage/local.go b/modules/storage/local.go index 701b0b1a9f3..5d5b06b648d 100644 --- a/modules/storage/local.go +++ b/modules/storage/local.go @@ -102,6 +102,10 @@ func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) if err := util.Rename(tmp.Name(), p); err != nil { return 0, err } + // Golang's tmp file (os.CreateTemp) always have 0o600 mode, so we need to change the file to follow the umask (as what Create/MkDir does) + if err := util.ApplyUmask(p, os.ModePerm); err != nil { + return 0, err + } tmpRemoved = true diff --git a/modules/util/file_unix.go b/modules/util/file_unix.go new file mode 100644 index 00000000000..ec9d4ec167a --- /dev/null +++ b/modules/util/file_unix.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +//go:build !windows + +package util + +import ( + "os" + + "golang.org/x/sys/unix" +) + +var defaultUmask int + +func init() { + // at the moment, the umask could only be gotten by calling unix.Umask(newUmask) + // use 0o077 as temp new umask to reduce the risks if this umask is used anywhere else before the correct umask is recovered + tempUmask := 0o077 + defaultUmask = unix.Umask(tempUmask) + unix.Umask(defaultUmask) +} + +func ApplyUmask(f string, newMode os.FileMode) error { + mod := newMode & ^os.FileMode(defaultUmask) + return os.Chmod(f, mod) +} diff --git a/modules/util/file_unix_test.go b/modules/util/file_unix_test.go new file mode 100644 index 00000000000..41311aa13f3 --- /dev/null +++ b/modules/util/file_unix_test.go @@ -0,0 +1,36 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +//go:build !windows + +package util + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestApplyUmask(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "test-filemode-") + assert.NoError(t, err) + + err = os.Chmod(f.Name(), 0o777) + assert.NoError(t, err) + st, err := os.Stat(f.Name()) + assert.NoError(t, err) + assert.EqualValues(t, 0o777, st.Mode().Perm()&0o777) + + oldDefaultUmask := defaultUmask + defaultUmask = 0o037 + defer func() { + defaultUmask = oldDefaultUmask + }() + err = ApplyUmask(f.Name(), os.ModePerm) + assert.NoError(t, err) + st, err = os.Stat(f.Name()) + assert.NoError(t, err) + assert.EqualValues(t, 0o740, st.Mode().Perm()&0o777) +} diff --git a/modules/util/file_windows.go b/modules/util/file_windows.go new file mode 100644 index 00000000000..6ad3e88ba5e --- /dev/null +++ b/modules/util/file_windows.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +//go:build windows + +package util + +import ( + "os" +) + +func ApplyUmask(f string, newMode os.FileMode) error { + // do nothing for Windows, because Windows doesn't use umask + return nil +}