@@ -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
189264func processImage (
190265 src , tmpDir , imageLayerCachePath string ,
0 commit comments