123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- package fileutils
- import (
- "fmt"
- "io"
- "os"
- "path/filepath"
- "syscall"
- )
- // CopyFile copies the file at source to dest
- func CopyFile(source string, dest string) error {
- si, err := os.Lstat(source)
- if err != nil {
- return err
- }
- st, ok := si.Sys().(*syscall.Stat_t)
- if !ok {
- return fmt.Errorf("could not convert to syscall.Stat_t")
- }
- uid := int(st.Uid)
- gid := int(st.Gid)
- // Handle symlinks
- if si.Mode()&os.ModeSymlink != 0 {
- target, err := os.Readlink(source)
- if err != nil {
- return err
- }
- if err := os.Symlink(target, dest); err != nil {
- return err
- }
- }
- // Handle device files
- if st.Mode&syscall.S_IFMT == syscall.S_IFBLK || st.Mode&syscall.S_IFMT == syscall.S_IFCHR {
- devMajor := int64(major(uint64(st.Rdev)))
- devMinor := int64(minor(uint64(st.Rdev)))
- mode := uint32(si.Mode() & 07777)
- if st.Mode&syscall.S_IFMT == syscall.S_IFBLK {
- mode |= syscall.S_IFBLK
- }
- if st.Mode&syscall.S_IFMT == syscall.S_IFCHR {
- mode |= syscall.S_IFCHR
- }
- if err := syscall.Mknod(dest, mode, int(mkdev(devMajor, devMinor))); err != nil {
- return err
- }
- }
- // Handle regular files
- if si.Mode().IsRegular() {
- sf, err := os.Open(source)
- if err != nil {
- return err
- }
- defer sf.Close()
- df, err := os.Create(dest)
- if err != nil {
- return err
- }
- defer df.Close()
- _, err = io.Copy(df, sf)
- if err != nil {
- return err
- }
- }
- // Chown the file
- if err := os.Lchown(dest, uid, gid); err != nil {
- return err
- }
- // Chmod the file
- if !(si.Mode()&os.ModeSymlink == os.ModeSymlink) {
- if err := os.Chmod(dest, si.Mode()); err != nil {
- return err
- }
- }
- return nil
- }
- // CopyDirectory copies the files under the source directory
- // to dest directory. The dest directory is created if it
- // does not exist.
- func CopyDirectory(source string, dest string) error {
- fi, err := os.Stat(source)
- if err != nil {
- return err
- }
- // Get owner.
- st, ok := fi.Sys().(*syscall.Stat_t)
- if !ok {
- return fmt.Errorf("could not convert to syscall.Stat_t")
- }
- // We have to pick an owner here anyway.
- if err := MkdirAllNewAs(dest, fi.Mode(), int(st.Uid), int(st.Gid)); err != nil {
- return err
- }
- return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- // Get the relative path
- relPath, err := filepath.Rel(source, path)
- if err != nil {
- return nil
- }
- if info.IsDir() {
- // Skip the source directory.
- if path != source {
- // Get the owner.
- st, ok := info.Sys().(*syscall.Stat_t)
- if !ok {
- return fmt.Errorf("could not convert to syscall.Stat_t")
- }
- uid := int(st.Uid)
- gid := int(st.Gid)
- if err := os.Mkdir(filepath.Join(dest, relPath), info.Mode()); err != nil {
- return err
- }
- if err := os.Lchown(filepath.Join(dest, relPath), uid, gid); err != nil {
- return err
- }
- }
- return nil
- }
- // Copy the file.
- if err := CopyFile(path, filepath.Join(dest, relPath)); err != nil {
- return err
- }
- return nil
- })
- }
- func major(device uint64) uint64 {
- return (device >> 8) & 0xfff
- }
- func minor(device uint64) uint64 {
- return (device & 0xff) | ((device >> 12) & 0xfff00)
- }
- func mkdev(major int64, minor int64) uint32 {
- return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
- }
|