Skip to content

Commit 35dd612

Browse files
committed
fix: add more resilient move
Fixes #12259 Signed-off-by: Mateusz Urbanek <[email protected]>
1 parent 8367583 commit 35dd612

File tree

1 file changed

+76
-1
lines changed

1 file changed

+76
-1
lines changed

pkg/imager/cache/cache.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import (
1111
"errors"
1212
"fmt"
1313
"io"
14+
"io/fs"
1415
"os"
1516
"path/filepath"
1617
"strings"
1718
"sync"
19+
"syscall"
1820
"time"
1921

2022
"github.com/dustin/go-humanize"
@@ -141,7 +143,7 @@ func Generate(images []string, platforms []string, insecure bool, imageLayerCach
141143
}
142144

143145
if flat {
144-
return os.Rename(tmpDir, dest)
146+
return move(tmpDir, dest)
145147
}
146148

147149
newImg := mutate.MediaType(empty.Image, types.OCIManifestSchema1)
@@ -185,6 +187,79 @@ func Generate(images []string, platforms []string, insecure bool, imageLayerCach
185187
return removeAll()
186188
}
187189

190+
func move(src, dest string) error {
191+
if err := os.Rename(src, dest); err == nil {
192+
return nil
193+
} else if !errors.Is(err, syscall.EXDEV) {
194+
// not a cross-device error - return it
195+
return err
196+
}
197+
198+
// cross-device: must copy+remove
199+
info, err := os.Stat(src)
200+
if err != nil {
201+
return err
202+
}
203+
204+
if info.IsDir() {
205+
if err := copyDir(src, dest); err != nil {
206+
return err
207+
}
208+
} else {
209+
if err := copyFile(src, dest, info.Mode()); err != nil {
210+
return err
211+
}
212+
}
213+
214+
return os.RemoveAll(src)
215+
}
216+
217+
func copyFile(src, dest string, perm fs.FileMode) error {
218+
in, err := os.Open(src)
219+
if err != nil {
220+
return err
221+
}
222+
defer in.Close() //nolint:errcheck
223+
224+
// Ensure destination directory exists
225+
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
226+
return err
227+
}
228+
229+
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, perm)
230+
if err != nil {
231+
return err
232+
}
233+
defer out.Close() //nolint:errcheck
234+
235+
if _, err = io.Copy(out, in); err != nil {
236+
return err
237+
}
238+
239+
return nil
240+
}
241+
242+
func copyDir(src, dest string) error {
243+
return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
244+
if err != nil {
245+
return err
246+
}
247+
248+
rel, err := filepath.Rel(src, path)
249+
if err != nil {
250+
return err
251+
}
252+
253+
target := filepath.Join(dest, rel)
254+
255+
if info.IsDir() {
256+
return os.MkdirAll(target, info.Mode())
257+
}
258+
259+
return copyFile(path, target, info.Mode())
260+
})
261+
}
262+
188263
//nolint:gocyclo,cyclop
189264
func processImage(
190265
src, tmpDir, imageLayerCachePath string,

0 commit comments

Comments
 (0)