github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/pkg/fileutil/fileutil.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fileutil 16 17 import ( 18 "fmt" 19 "io" 20 "os" 21 "path/filepath" 22 "syscall" 23 "time" 24 25 "github.com/coreos/rkt/pkg/uid" 26 27 "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/pkg/device" 28 ) 29 30 func CopyRegularFile(src, dest string) (err error) { 31 srcFile, err := os.Open(src) 32 if err != nil { 33 return err 34 } 35 defer srcFile.Close() 36 destFile, err := os.Create(dest) 37 if err != nil { 38 return err 39 } 40 defer func() { 41 e := destFile.Close() 42 if err == nil { 43 err = e 44 } 45 }() 46 if _, err := io.Copy(destFile, srcFile); err != nil { 47 return err 48 } 49 return nil 50 } 51 52 func CopySymlink(src, dest string) error { 53 symTarget, err := os.Readlink(src) 54 if err != nil { 55 return err 56 } 57 if err := os.Symlink(symTarget, dest); err != nil { 58 return err 59 } 60 return nil 61 } 62 63 func CopyTree(src, dest string, uidRange *uid.UidRange) error { 64 cleanSrc := filepath.Clean(src) 65 66 dirs := make(map[string][]syscall.Timespec) 67 copyWalker := func(path string, info os.FileInfo, err error) error { 68 if err != nil { 69 return err 70 } 71 rootLess := path[len(cleanSrc):] 72 target := filepath.Join(dest, rootLess) 73 mode := info.Mode() 74 switch { 75 case mode.IsDir(): 76 err := os.Mkdir(target, mode.Perm()) 77 if err != nil { 78 return err 79 } 80 81 dir, err := os.Open(target) 82 if err != nil { 83 return err 84 } 85 if err := dir.Chmod(mode); err != nil { 86 dir.Close() 87 return err 88 } 89 dir.Close() 90 case mode.IsRegular(): 91 if err := CopyRegularFile(path, target); err != nil { 92 return err 93 } 94 case mode&os.ModeSymlink == os.ModeSymlink: 95 if err := CopySymlink(path, target); err != nil { 96 return err 97 } 98 case mode&os.ModeCharDevice == os.ModeCharDevice: 99 stat := syscall.Stat_t{} 100 if err := syscall.Stat(path, &stat); err != nil { 101 return err 102 } 103 104 dev := device.Makedev(device.Major(stat.Rdev), device.Minor(stat.Rdev)) 105 mode := uint32(mode) | syscall.S_IFCHR 106 if err := syscall.Mknod(target, mode, int(dev)); err != nil { 107 return err 108 } 109 case mode&os.ModeDevice == os.ModeDevice: 110 stat := syscall.Stat_t{} 111 if err := syscall.Stat(path, &stat); err != nil { 112 return err 113 } 114 115 dev := device.Makedev(device.Major(stat.Rdev), device.Minor(stat.Rdev)) 116 mode := uint32(mode) | syscall.S_IFBLK 117 if err := syscall.Mknod(target, mode, int(dev)); err != nil { 118 return err 119 } 120 case mode&os.ModeNamedPipe == os.ModeNamedPipe: 121 if err := syscall.Mkfifo(target, uint32(mode)); err != nil { 122 return err 123 } 124 default: 125 return fmt.Errorf("unsupported mode: %v", mode) 126 } 127 128 var srcUid = info.Sys().(*syscall.Stat_t).Uid 129 var srcGid = info.Sys().(*syscall.Stat_t).Gid 130 131 shiftedUid, shiftedGid, err := uidRange.ShiftRange(srcUid, srcGid) 132 if err != nil { 133 return err 134 } 135 136 if err := os.Lchown(target, int(shiftedUid), int(shiftedGid)); err != nil { 137 return err 138 } 139 140 // lchown(2) says that, depending on the linux kernel version, it 141 // can change the file's mode also if executed as root. So call 142 // os.Chmod after it. 143 if mode&os.ModeSymlink != os.ModeSymlink { 144 if err := os.Chmod(target, mode); err != nil { 145 return err 146 } 147 } 148 149 ts, err := pathToTimespec(path) 150 if err != nil { 151 return err 152 } 153 154 if mode.IsDir() { 155 dirs[target] = ts 156 } 157 if mode&os.ModeSymlink != os.ModeSymlink { 158 if err := syscall.UtimesNano(target, ts); err != nil { 159 return err 160 } 161 } else { 162 if err := LUtimesNano(target, ts); err != nil { 163 return err 164 } 165 } 166 167 return nil 168 } 169 170 if err := filepath.Walk(cleanSrc, copyWalker); err != nil { 171 return err 172 } 173 174 // Restore dirs atime and mtime. This has to be done after copying 175 // as a file copying will change its parent directory's times. 176 for dirPath, ts := range dirs { 177 if err := syscall.UtimesNano(dirPath, ts); err != nil { 178 return err 179 } 180 } 181 182 return nil 183 } 184 185 func pathToTimespec(name string) ([]syscall.Timespec, error) { 186 fi, err := os.Lstat(name) 187 if err != nil { 188 return nil, err 189 } 190 mtime := fi.ModTime() 191 stat := fi.Sys().(*syscall.Stat_t) 192 atime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) 193 return []syscall.Timespec{TimeToTimespec(atime), TimeToTimespec(mtime)}, nil 194 } 195 196 // TODO(sgotti) use UTIMES_OMIT on linux if Time.IsZero ? 197 func TimeToTimespec(time time.Time) (ts syscall.Timespec) { 198 nsec := int64(0) 199 if !time.IsZero() { 200 nsec = time.UnixNano() 201 } 202 return syscall.NsecToTimespec(nsec) 203 }