diff --git a/cmd/kubeadm/app/phases/upgrade/staticpods.go b/cmd/kubeadm/app/phases/upgrade/staticpods.go index e5c88ac6f9e..54a0e63e120 100644 --- a/cmd/kubeadm/app/phases/upgrade/staticpods.go +++ b/cmd/kubeadm/app/phases/upgrade/staticpods.go @@ -123,7 +123,7 @@ func NewKubeStaticPodPathManagerUsingTempDirs(kubernetesDir, patchesDir string, // MoveFile should move a file from oldPath to newPath func (spm *KubeStaticPodPathManager) MoveFile(oldPath, newPath string) error { - return os.Rename(oldPath, newPath) + return kubeadmutil.MoveFile(oldPath, newPath) } // KubernetesDir should point to the directory Kubernetes owns for storing various configuration files diff --git a/cmd/kubeadm/app/util/copy.go b/cmd/kubeadm/app/util/copy.go index f5c1b106df8..f3562e3b6b9 100644 --- a/cmd/kubeadm/app/util/copy.go +++ b/cmd/kubeadm/app/util/copy.go @@ -17,19 +17,56 @@ limitations under the License. package util import ( + "io" "os" + "strings" + + "github.com/pkg/errors" + + "k8s.io/klog/v2" ) // CopyFile copies a file from src to dest. func CopyFile(src, dest string) error { - fileInfo, err := os.Stat(src) + sourceFileInfo, err := os.Stat(src) if err != nil { return err } - contents, err := os.ReadFile(src) + + sourceFile, err := os.Open(src) if err != nil { return err } - err = os.WriteFile(dest, contents, fileInfo.Mode()) + defer func() { + _ = sourceFile.Close() + }() + + destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, sourceFileInfo.Mode()) + if err != nil { + return err + } + defer func() { + _ = destFile.Close() + }() + + _, err = io.Copy(destFile, sourceFile) + + return err +} + +// MoveFile moves a file from src to dest. +func MoveFile(src, dest string) error { + err := os.Rename(src, dest) + if err != nil && strings.Contains(err.Error(), "invalid cross-device link") { + // When calling os.Rename(), an "invalid cross-device link" error may occur + // if the source and destination files are on different file systems. + // In this case, the file is moved by copying and then deleting the source file, + // although it is less efficient than os.Rename(). + klog.V(4).Infof("cannot rename %v to %v due to %v, attempting an alternative method", src, dest, err) + if err := CopyFile(src, dest); err != nil { + return errors.Wrapf(err, "failed to copy file %v to %v", src, dest) + } + return os.Remove(src) + } return err }