Skip to content

Commit

Permalink
fix: use copy when a rename spans volumes (influxdata#23785)
Browse files Browse the repository at this point in the history
When a file rename fails with EXDEV
(cross device or volume error), copy the
file and delete the original instead

Differs from master branch by overwriting
existing files instead of erring.

closes influxdata#22997
  • Loading branch information
davidby-influx authored and chengshiwen committed Aug 11, 2024
1 parent e46db78 commit 72e3f53
Showing 1 changed file with 62 additions and 2 deletions.
64 changes: 62 additions & 2 deletions pkg/file/file_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
package file

import (
"errors"
"fmt"
"io"
"os"
"syscall"

errors2 "github.com/influxdata/influxdb/pkg/errors"
)

func SyncDir(dirName string) error {
Expand All @@ -29,7 +34,62 @@ func SyncDir(dirName string) error {
return dir.Close()
}

// RenameFile will rename the source to target using os function.
// RenameFileWithReplacement will replace any existing file at newpath with the contents
// of oldpath. It works also if it the rename spans over several file systems.
//
// If no file already exists at newpath, newpath will be created using the contents
// of oldpath. If this function returns successfully, the contents of newpath will
// be identical to oldpath, and oldpath will be removed.
func RenameFileWithReplacement(oldpath, newpath string) error {
if err := os.Rename(oldpath, newpath); !errors.Is(err, syscall.EXDEV) {
// note: also includes err == nil
return err
}

// move over filesystem boundaries, we have to copy.
// (if there was another error, it will likely fail a second time)
return MoveFileWithReplacement(oldpath, newpath)

}

// RenameFile renames oldpath to newpath, returning an error if newpath already
// exists. If this function returns successfully, the contents of newpath will
// be identical to oldpath, and oldpath will be removed.
func RenameFile(oldpath, newpath string) error {
return os.Rename(oldpath, newpath)
return RenameFileWithReplacement(oldpath, newpath)
}

func copyFile(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return err
}

out, err := os.Create(dst)
if err != nil {
return err
}

defer errors2.Capture(&err, out.Close)()

defer errors2.Capture(&err, in.Close)()

if _, err = io.Copy(out, in); err != nil {
return err
}

return out.Sync()
}

// MoveFileWithReplacement copies the file contents at `src` to `dst`.
// and deletes `src` on success.
//
// If the file at `dst` already exists, it will be truncated and its contents
// overwritten.
func MoveFileWithReplacement(src, dst string) error {
if err := copyFile(src, dst); err != nil {
return fmt.Errorf("copy: %w", err)
}

return os.Remove(src)
}

0 comments on commit 72e3f53

Please sign in to comment.