1package fileutils 2 3import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 "syscall" 9) 10 11// CopyFile copies the file at source to dest 12func CopyFile(source string, dest string) error { 13 si, err := os.Lstat(source) 14 if err != nil { 15 return err 16 } 17 18 st, ok := si.Sys().(*syscall.Stat_t) 19 if !ok { 20 return fmt.Errorf("could not convert to syscall.Stat_t") 21 } 22 23 uid := int(st.Uid) 24 gid := int(st.Gid) 25 26 // Handle symlinks 27 if si.Mode()&os.ModeSymlink != 0 { 28 target, err := os.Readlink(source) 29 if err != nil { 30 return err 31 } 32 if err := os.Symlink(target, dest); err != nil { 33 return err 34 } 35 } 36 37 // Handle device files 38 if st.Mode&syscall.S_IFMT == syscall.S_IFBLK || st.Mode&syscall.S_IFMT == syscall.S_IFCHR { 39 devMajor := int64(major(uint64(st.Rdev))) 40 devMinor := int64(minor(uint64(st.Rdev))) 41 mode := uint32(si.Mode() & 07777) 42 if st.Mode&syscall.S_IFMT == syscall.S_IFBLK { 43 mode |= syscall.S_IFBLK 44 } 45 if st.Mode&syscall.S_IFMT == syscall.S_IFCHR { 46 mode |= syscall.S_IFCHR 47 } 48 if err := syscall.Mknod(dest, mode, int(mkdev(devMajor, devMinor))); err != nil { 49 return err 50 } 51 } 52 53 // Handle regular files 54 if si.Mode().IsRegular() { 55 sf, err := os.Open(source) 56 if err != nil { 57 return err 58 } 59 defer sf.Close() 60 61 df, err := os.Create(dest) 62 if err != nil { 63 return err 64 } 65 defer df.Close() 66 67 _, err = io.Copy(df, sf) 68 if err != nil { 69 return err 70 } 71 } 72 73 // Chown the file 74 if err := os.Lchown(dest, uid, gid); err != nil { 75 return err 76 } 77 78 // Chmod the file 79 if !(si.Mode()&os.ModeSymlink == os.ModeSymlink) { 80 if err := os.Chmod(dest, si.Mode()); err != nil { 81 return err 82 } 83 } 84 85 return nil 86} 87 88// CopyDirectory copies the files under the source directory 89// to dest directory. The dest directory is created if it 90// does not exist. 91func CopyDirectory(source string, dest string) error { 92 fi, err := os.Stat(source) 93 if err != nil { 94 return err 95 } 96 97 // Get owner. 98 st, ok := fi.Sys().(*syscall.Stat_t) 99 if !ok { 100 return fmt.Errorf("could not convert to syscall.Stat_t") 101 } 102 103 // We have to pick an owner here anyway. 104 if err := MkdirAllNewAs(dest, fi.Mode(), int(st.Uid), int(st.Gid)); err != nil { 105 return err 106 } 107 108 return filepath.Walk(source, func(path string, info os.FileInfo, err error) error { 109 if err != nil { 110 return err 111 } 112 113 // Get the relative path 114 relPath, err := filepath.Rel(source, path) 115 if err != nil { 116 return nil 117 } 118 119 if info.IsDir() { 120 // Skip the source directory. 121 if path != source { 122 // Get the owner. 123 st, ok := info.Sys().(*syscall.Stat_t) 124 if !ok { 125 return fmt.Errorf("could not convert to syscall.Stat_t") 126 } 127 128 uid := int(st.Uid) 129 gid := int(st.Gid) 130 131 if err := os.Mkdir(filepath.Join(dest, relPath), info.Mode()); err != nil { 132 return err 133 } 134 135 if err := os.Lchown(filepath.Join(dest, relPath), uid, gid); err != nil { 136 return err 137 } 138 } 139 return nil 140 } 141 142 return CopyFile(path, filepath.Join(dest, relPath)) 143 }) 144} 145 146// Gives a number indicating the device driver to be used to access the passed device 147func major(device uint64) uint64 { 148 return (device >> 8) & 0xfff 149} 150 151// Gives a number that serves as a flag to the device driver for the passed device 152func minor(device uint64) uint64 { 153 return (device & 0xff) | ((device >> 12) & 0xfff00) 154} 155 156func mkdev(major int64, minor int64) uint32 { 157 return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff)) 158} 159