mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-04-22 02:38:32 -05:00
Bump reva
This commit is contained in:
+11
-1
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package renameio
|
||||
|
||||
@@ -86,3 +85,14 @@ func WithReplaceOnClose() Option {
|
||||
c.renameOnClose = true
|
||||
})
|
||||
}
|
||||
|
||||
// WithRoot specifies a root directory to use when working with files.
|
||||
// See [os.Root] and https://go.dev/blog/osroot for more details.
|
||||
//
|
||||
// When WithRoot is used, WithTempDir (and the $TMPDIR environment variable) are
|
||||
// ignored, as temporary files must be created in the specified root directory.
|
||||
func WithRoot(root *os.Root) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.root = root
|
||||
})
|
||||
}
|
||||
|
||||
+114
-16
@@ -13,13 +13,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package renameio
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -29,10 +27,10 @@ import (
|
||||
const defaultPerm os.FileMode = 0o600
|
||||
|
||||
// nextrandom is a function generating a random number.
|
||||
var nextrandom = rand.Int63
|
||||
var nextrandom = rand.Int64
|
||||
|
||||
// openTempFile creates a randomly named file and returns an open handle. It is
|
||||
// similar to ioutil.TempFile except that the directory must be given, the file
|
||||
// similar to os.CreateTemp except that the directory must be given, the file
|
||||
// permissions can be controlled and patterns in the name are not supported.
|
||||
// The name is always suffixed with a random number.
|
||||
func openTempFile(dir, name string, perm os.FileMode) (*os.File, error) {
|
||||
@@ -58,6 +56,33 @@ func openTempFile(dir, name string, perm os.FileMode) (*os.File, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// openTempFileRoot creates a randomly named file in root and returns an open
|
||||
// handle. It is similar to os.CreateTemp except that the directory must be
|
||||
// given, the file permissions can be controlled and patterns in the name are
|
||||
// not supported. The name is always suffixed with a random number.
|
||||
func openTempFileRoot(root *os.Root, name string, perm os.FileMode) (string, *os.File, error) {
|
||||
prefix := name
|
||||
|
||||
for attempt := 0; ; {
|
||||
// Generate a reasonably random name which is unlikely to already
|
||||
// exist. O_EXCL ensures that existing files generate an error.
|
||||
name := prefix + strconv.FormatInt(nextrandom(), 10)
|
||||
|
||||
f, err := root.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm)
|
||||
if !os.IsExist(err) {
|
||||
return name, f, err
|
||||
}
|
||||
|
||||
if attempt++; attempt > 10000 {
|
||||
return "", nil, &os.PathError{
|
||||
Op: "tempfile",
|
||||
Path: name,
|
||||
Err: os.ErrExist,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TempDir checks whether os.TempDir() can be used as a temporary directory for
|
||||
// later atomically replacing files within dest. If no (os.TempDir() resides on
|
||||
// a different mount point), dest is returned.
|
||||
@@ -83,7 +108,7 @@ func tempDir(dir, dest string) string {
|
||||
// the TMPDIR environment variable.
|
||||
tmpdir := os.TempDir()
|
||||
|
||||
testsrc, err := ioutil.TempFile(tmpdir, "."+filepath.Base(dest))
|
||||
testsrc, err := os.CreateTemp(tmpdir, "."+filepath.Base(dest))
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
@@ -95,7 +120,7 @@ func tempDir(dir, dest string) string {
|
||||
}()
|
||||
testsrc.Close()
|
||||
|
||||
testdest, err := ioutil.TempFile(filepath.Dir(dest), "."+filepath.Base(dest))
|
||||
testdest, err := os.CreateTemp(filepath.Dir(dest), "."+filepath.Base(dest))
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
@@ -118,6 +143,8 @@ type PendingFile struct {
|
||||
done bool
|
||||
closed bool
|
||||
replaceOnClose bool
|
||||
root *os.Root
|
||||
tmpname string
|
||||
}
|
||||
|
||||
// Cleanup is a no-op if CloseAtomicallyReplace succeeded, and otherwise closes
|
||||
@@ -134,8 +161,14 @@ func (t *PendingFile) Cleanup() error {
|
||||
if !t.closed {
|
||||
closeErr = t.File.Close()
|
||||
}
|
||||
if err := os.Remove(t.Name()); err != nil {
|
||||
return err
|
||||
if t.root != nil {
|
||||
if err := t.root.Remove(t.tmpname); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := os.Remove(t.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.done = true
|
||||
return closeErr
|
||||
@@ -163,8 +196,14 @@ func (t *PendingFile) CloseAtomicallyReplace() error {
|
||||
if err := t.File.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(t.Name(), t.path); err != nil {
|
||||
return err
|
||||
if t.root != nil {
|
||||
if err := t.root.Rename(t.tmpname, t.path); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := os.Rename(t.Name(), t.path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
t.done = true
|
||||
return nil
|
||||
@@ -200,6 +239,7 @@ type config struct {
|
||||
ignoreUmask bool
|
||||
chmod *os.FileMode
|
||||
renameOnClose bool
|
||||
root *os.Root
|
||||
}
|
||||
|
||||
// NewPendingFile creates a temporary file destined to atomically creating or
|
||||
@@ -227,8 +267,15 @@ func NewPendingFile(path string, opts ...Option) (*PendingFile, error) {
|
||||
}
|
||||
|
||||
if cfg.attemptPermCopy {
|
||||
var existing os.FileInfo
|
||||
var err error
|
||||
if cfg.root != nil {
|
||||
existing, err = cfg.root.Lstat(cfg.path)
|
||||
} else {
|
||||
existing, err = os.Lstat(cfg.path)
|
||||
}
|
||||
// Try to determine permissions from an existing file.
|
||||
if existing, err := os.Lstat(cfg.path); err == nil && existing.Mode().IsRegular() {
|
||||
if err == nil && existing.Mode().IsRegular() {
|
||||
perm := existing.Mode() & os.ModePerm
|
||||
cfg.chmod = &perm
|
||||
|
||||
@@ -240,7 +287,14 @@ func NewPendingFile(path string, opts ...Option) (*PendingFile, error) {
|
||||
}
|
||||
}
|
||||
|
||||
f, err := openTempFile(tempDir(cfg.dir, cfg.path), "."+filepath.Base(cfg.path), cfg.createPerm)
|
||||
var f *os.File
|
||||
var err error
|
||||
var tmpname string
|
||||
if cfg.root != nil {
|
||||
tmpname, f, err = openTempFileRoot(cfg.root, "."+filepath.Base(cfg.path), cfg.createPerm)
|
||||
} else {
|
||||
f, err = openTempFile(tempDir(cfg.dir, cfg.path), "."+filepath.Base(cfg.path), cfg.createPerm)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -255,7 +309,13 @@ func NewPendingFile(path string, opts ...Option) (*PendingFile, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return &PendingFile{File: f, path: cfg.path, replaceOnClose: cfg.renameOnClose}, nil
|
||||
return &PendingFile{
|
||||
File: f,
|
||||
path: cfg.path,
|
||||
replaceOnClose: cfg.renameOnClose,
|
||||
root: cfg.root,
|
||||
tmpname: tmpname,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Symlink wraps os.Symlink, replacing an existing symlink with the same name
|
||||
@@ -267,9 +327,9 @@ func Symlink(oldname, newname string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need to use ioutil.TempDir, as we cannot overwrite a ioutil.TempFile,
|
||||
// We need to use os.MkdirTemp, as we cannot overwrite a os.CreateTemp file,
|
||||
// and removing+symlinking creates a TOCTOU race.
|
||||
d, err := ioutil.TempDir(filepath.Dir(newname), "."+filepath.Base(newname))
|
||||
d, err := os.MkdirTemp(filepath.Dir(newname), "."+filepath.Base(newname))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -292,3 +352,41 @@ func Symlink(oldname, newname string) error {
|
||||
cleanup = false
|
||||
return os.RemoveAll(d)
|
||||
}
|
||||
|
||||
// SymlinkRoot wraps os.Symlink, replacing an existing symlink with the same
|
||||
// name atomically (os.Symlink fails when newname already exists, at least on
|
||||
// Linux).
|
||||
func SymlinkRoot(root *os.Root, oldname, newname string) error {
|
||||
// Fast path: if newname does not exist yet, we can skip the whole dance
|
||||
// below.
|
||||
if err := root.Symlink(oldname, newname); err == nil || !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need to use os.MkdirTemp, as we cannot overwrite a os.CreateTemp file,
|
||||
// and removing+symlinking creates a TOCTOU race.
|
||||
//
|
||||
// There is no os.Root-compatible os.MkdirTemp, so we use the path directly.
|
||||
d, err := os.MkdirTemp(root.Name(), "."+filepath.Base(newname))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cleanup := true
|
||||
defer func() {
|
||||
if cleanup {
|
||||
os.RemoveAll(d)
|
||||
}
|
||||
}()
|
||||
|
||||
symlink := filepath.Join(filepath.Base(d), "tmp.symlink")
|
||||
if err := root.Symlink(oldname, symlink); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := root.Rename(symlink, newname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cleanup = false
|
||||
return os.RemoveAll(d)
|
||||
}
|
||||
|
||||
+1
-2
@@ -13,13 +13,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package renameio
|
||||
|
||||
import "os"
|
||||
|
||||
// WriteFile mirrors ioutil.WriteFile, replacing an existing file with the same
|
||||
// WriteFile mirrors os.WriteFile, replacing an existing file with the same
|
||||
// name atomically.
|
||||
func WriteFile(filename string, data []byte, perm os.FileMode, opts ...Option) error {
|
||||
opts = append([]Option{
|
||||
|
||||
Reference in New Issue
Block a user