Skip to content

Commit

Permalink
Add CopyFile utility
Browse files Browse the repository at this point in the history
This commit ports utils.CopyFile utility from
former Elemental Toolkit. This now integrated as part
of the FS utilities under sys package.

Signed-off-by: David Cassany <dcassany@suse.com>
  • Loading branch information
davidcassany committed Mar 5, 2025
1 parent 8f26e42 commit 2fdeb61
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
55 changes: 55 additions & 0 deletions pkg/sys/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package sys
import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
Expand Down Expand Up @@ -355,3 +356,57 @@ func readDir(vfs FS, dirname string) ([]fs.DirEntry, error) {
sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
return dirs, nil
}

// CopyFile Copies source file to target file using Fs interface. If target
// is directory source is copied into that directory using source name file.
// File mode is preserved.
func CopyFile(fs FS, source string, target string) error {
return ConcatFiles(fs, []string{source}, target)
}

// ConcatFiles Copies source files to target file using Fs interface.
// Source files are concatenated into target file in the given order.
// If target is a directory source is copied into that directory using
// 1st source name file. The result keeps the file mode of the 1st source.
func ConcatFiles(fs FS, sources []string, target string) (err error) {
if len(sources) == 0 {
return fmt.Errorf("empty sources list")
}
if dir, _ := IsDir(fs, target); dir {
target = filepath.Join(target, filepath.Base(sources[0]))
}
fInf, err := fs.Stat(sources[0])
if err != nil {
return err
}

targetFile, err := fs.Create(target)
if err != nil {
return err
}
defer func() {
if err == nil {
err = targetFile.Close()
} else {
_ = fs.Remove(target)
}
}()

var sourceFile *os.File
for _, source := range sources {
sourceFile, err = fs.OpenFile(source, os.O_RDONLY, FilePerm)
if err != nil {
return err
}
_, err = io.Copy(targetFile, sourceFile)
if err != nil {
return err
}
err = sourceFile.Close()
if err != nil {
return err
}
}

return fs.Chmod(target, fInf.Mode())
}
47 changes: 47 additions & 0 deletions pkg/sys/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,51 @@ var _ = Describe("FS", Label("fs"), func() {
Expect(foundPaths).To(Equal(currentPahts))
})
})
Describe("CopyFile", func() {
It("Copies source file to target file", func() {
err := sys.MkdirAll(tfs, "/some", sys.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = tfs.Create("/some/file")
Expect(err).ShouldNot(HaveOccurred())
_, err = tfs.Stat("/some/otherfile")
Expect(err).Should(HaveOccurred())
Expect(sys.CopyFile(tfs, "/some/file", "/some/otherfile")).ShouldNot(HaveOccurred())
e, err := sys.Exists(tfs, "/some/otherfile")
Expect(err).ShouldNot(HaveOccurred())
Expect(e).To(BeTrue())
})
It("Copies source file to target folder", func() {
err := sys.MkdirAll(tfs, "/some", sys.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = sys.MkdirAll(tfs, "/someotherfolder", sys.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = tfs.Create("/some/file")
Expect(err).ShouldNot(HaveOccurred())
_, err = tfs.Stat("/someotherfolder/file")
Expect(err).Should(HaveOccurred())
Expect(sys.CopyFile(tfs, "/some/file", "/someotherfolder")).ShouldNot(HaveOccurred())
e, err := sys.Exists(tfs, "/someotherfolder/file")
Expect(err).ShouldNot(HaveOccurred())
Expect(e).To(BeTrue())
})
It("Fails to open non existing file", func() {
err := sys.MkdirAll(tfs, "/some", sys.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
Expect(sys.CopyFile(tfs, "/some/file", "/some/otherfile")).NotTo(BeNil())
_, err = tfs.Stat("/some/otherfile")
Expect(err).NotTo(BeNil())
})
It("Fails to copy on non writable target", func() {
err := sys.MkdirAll(tfs, "/some", sys.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
tfs.Create("/some/file")
_, err = tfs.Stat("/some/otherfile")
Expect(err).NotTo(BeNil())
tfs, err = sysmock.ReadOnlyTestFS(tfs)
Expect(err).NotTo(HaveOccurred())
Expect(sys.CopyFile(tfs, "/some/file", "/some/otherfile")).NotTo(BeNil())
_, err = tfs.Stat("/some/otherfile")
Expect(err).NotTo(BeNil())
})
})
})

0 comments on commit 2fdeb61

Please sign in to comment.