github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/cpio/fs_unix.go (about) 1 // Copyright 2013-2017 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cpio 6 7 import ( 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "syscall" 14 15 "github.com/u-root/u-root/pkg/uio" 16 "golang.org/x/sys/unix" 17 ) 18 19 // Linux mode_t bits. 20 const ( 21 modeTypeMask = 0170000 22 modeSocket = 0140000 23 modeSymlink = 0120000 24 modeFile = 0100000 25 modeBlock = 0060000 26 modeDir = 0040000 27 modeChar = 0020000 28 modeFIFO = 0010000 29 modeSUID = 0004000 30 modeSGID = 0002000 31 modeSticky = 0001000 32 modePermissions = 0000777 33 ) 34 35 var modeMap = map[uint64]os.FileMode{ 36 modeSocket: os.ModeSocket, 37 modeSymlink: os.ModeSymlink, 38 modeFile: 0, 39 modeBlock: os.ModeDevice, 40 modeDir: os.ModeDir, 41 modeChar: os.ModeCharDevice, 42 modeFIFO: os.ModeNamedPipe, 43 } 44 45 // setModes sets the modes, changing the easy ones first and the harder ones last. 46 // In this way, we set as much as we can before bailing out. 47 // N.B.: if you set something with S_ISUID, then change the owner, 48 // the kernel (Linux, OSX, etc.) clears S_ISUID (a good idea). So, the simple thing: 49 // Do the chmod operations in order of difficulty, and give up as soon as we fail. 50 // Set the basic permissions -- not including SUID, GUID, etc. 51 // Set the times 52 // Set the owner 53 // Set ALL the mode bits, in case we need to do SUID, etc. If we could not 54 // set the owner, we won't even try this operation of course, so we won't 55 // have SUID incorrectly set for the wrong user. 56 func setModes(r Record) error { 57 if err := os.Chmod(r.Name, toFileMode(r)&os.ModePerm); err != nil { 58 return err 59 } 60 /*if err := os.Chtimes(r.Name, time.Time{}, time.Unix(int64(r.MTime), 0)); err != nil { 61 return err 62 }*/ 63 if err := os.Chown(r.Name, int(r.UID), int(r.GID)); err != nil { 64 return err 65 } 66 if err := os.Chmod(r.Name, toFileMode(r)); err != nil { 67 return err 68 } 69 return nil 70 } 71 72 func toFileMode(r Record) os.FileMode { 73 m := os.FileMode(perm(r)) 74 if r.Mode&unix.S_ISUID != 0 { 75 m |= os.ModeSetuid 76 } 77 if r.Mode&unix.S_ISGID != 0 { 78 m |= os.ModeSetgid 79 } 80 if r.Mode&unix.S_ISVTX != 0 { 81 m |= os.ModeSticky 82 } 83 return m 84 } 85 86 func perm(r Record) uint32 { 87 return uint32(r.Mode) & modePermissions 88 } 89 90 func dev(r Record) int { 91 return int(r.Rmajor<<8 | r.Rminor) 92 } 93 94 func linuxModeToFileType(m uint64) (os.FileMode, error) { 95 if t, ok := modeMap[m&modeTypeMask]; ok { 96 return t, nil 97 } 98 return 0, fmt.Errorf("invalid file type %#o", m&modeTypeMask) 99 } 100 101 // CreateFile creates a local file for f relative to the current working 102 // directory. 103 // 104 // CreateFile will attempt to set all metadata for the file, including 105 // ownership, times, and permissions. 106 func CreateFile(f Record) error { 107 return CreateFileInRoot(f, ".", true) 108 } 109 110 // CreateFileInRoot creates a local file for f relative to rootDir. 111 // 112 // It will attempt to set all metadata for the file, including ownership, 113 // times, and permissions. If these fail, it only returns an error if 114 // forcePriv is true. 115 // 116 // Block and char device creation will only return error if forcePriv is true. 117 func CreateFileInRoot(f Record, rootDir string, forcePriv bool) error { 118 m, err := linuxModeToFileType(f.Mode) 119 if err != nil { 120 return err 121 } 122 123 f.Name = filepath.Clean(filepath.Join(rootDir, f.Name)) 124 dir := filepath.Dir(f.Name) 125 // The problem: many cpio archives do not specify the directories and 126 // hence the permissions. They just specify the whole path. In order 127 // to create files in these directories, we have to make them at least 128 // mode 755. 129 if _, err := os.Stat(dir); os.IsNotExist(err) && len(dir) > 0 { 130 if err := os.MkdirAll(dir, 0755); err != nil { 131 return fmt.Errorf("CreateFileInRoot %q: %v", f.Name, err) 132 } 133 } 134 135 switch m { 136 case os.ModeSocket, os.ModeNamedPipe: 137 return fmt.Errorf("%q: type %v: cannot create IPC endpoints", f.Name, m) 138 139 case os.ModeSymlink: 140 content, err := ioutil.ReadAll(uio.Reader(f)) 141 if err != nil { 142 return err 143 } 144 return os.Symlink(string(content), f.Name) 145 146 case os.FileMode(0): 147 nf, err := os.Create(f.Name) 148 if err != nil { 149 return err 150 } 151 defer nf.Close() 152 if _, err := io.Copy(nf, uio.Reader(f)); err != nil { 153 return err 154 } 155 156 case os.ModeDir: 157 if err := os.MkdirAll(f.Name, toFileMode(f)); err != nil { 158 return err 159 } 160 161 case os.ModeDevice: 162 if err := mknod(f.Name, perm(f)|syscall.S_IFBLK, dev(f)); err != nil && forcePriv { 163 return err 164 } 165 166 case os.ModeCharDevice: 167 if err := mknod(f.Name, perm(f)|syscall.S_IFCHR, dev(f)); err != nil && forcePriv { 168 return err 169 } 170 171 default: 172 return fmt.Errorf("%v: Unknown type %#o", f.Name, m) 173 } 174 175 if err := setModes(f); err != nil && forcePriv { 176 return err 177 } 178 return nil 179 } 180 181 // Inumber and devnumbers are unique to Unix-like 182 // operating systems. You can not uniquely disambiguate a file in a 183 // Unix system with just an inumber, you need a device number too. 184 // To handle hard links (unique to Unix) we need to figure out if a 185 // given file has been seen before. To do this we see if a file has the 186 // same [dev,ino] tuple as one we have seen. If so, we won't bother 187 // reading it in. 188 189 type devInode struct { 190 dev uint64 191 ino uint64 192 } 193 194 // A Recorder is a structure that contains variables used to calculate 195 // file parameters such as inode numbers for a CPIO file. The life-time 196 // of a Record structure is meant to be the same as the construction of a 197 // single CPIO archive. Do not reuse between CPIOs if you don't know what 198 // you're doing. 199 type Recorder struct { 200 inodeMap map[devInode]Info 201 inumber uint64 202 } 203 204 // Certain elements of the file can not be set by cpio: 205 // the Inode # 206 // the Dev 207 // maintaining these elements leaves us with a non-reproducible 208 // output stream. In this function, we figure out what inumber 209 // we need to use, and clear out anything we can. 210 // We always zero the Dev. 211 // We try to find the matching inode. If found, we use its inumber. 212 // If not, we get a new inumber for it and save the inode away. 213 // This eliminates two of the messier parts of creating reproducible 214 // output streams. 215 func (r *Recorder) inode(i Info) (Info, bool) { 216 d := devInode{dev: i.Dev, ino: i.Ino} 217 i.Dev = 0 218 219 if d, ok := r.inodeMap[d]; ok { 220 i.Ino = d.Ino 221 return i, true 222 } 223 224 i.Ino = r.inumber 225 r.inumber++ 226 r.inodeMap[d] = i 227 228 return i, false 229 } 230 231 // GetRecord returns a cpio Record for the given path on the local file system. 232 // 233 // GetRecord does not follow symlinks. If path is a symlink, the record 234 // returned will reflect that symlink. 235 func (r *Recorder) GetRecord(path string) (Record, error) { 236 fi, err := os.Lstat(path) 237 if err != nil { 238 return Record{}, err 239 } 240 241 sys := fi.Sys().(*syscall.Stat_t) 242 info, done := r.inode(sysInfo(path, sys)) 243 244 switch fi.Mode() & os.ModeType { 245 case 0: // Regular file. 246 if done { 247 return Record{Info: info}, nil 248 } 249 return Record{Info: info, ReaderAt: uio.NewLazyFile(path)}, nil 250 251 case os.ModeSymlink: 252 linkname, err := os.Readlink(path) 253 if err != nil { 254 return Record{}, err 255 } 256 return StaticRecord([]byte(linkname), info), nil 257 258 default: 259 return StaticRecord(nil, info), nil 260 } 261 } 262 263 // NewRecorder creates a new Recorder. 264 // 265 // A recorder is a structure that contains variables used to calculate 266 // file parameters such as inode numbers for a CPIO file. The life-time 267 // of a Record structure is meant to be the same as the construction of a 268 // single CPIO archive. Do not reuse between CPIOs if you don't know what 269 // you're doing. 270 func NewRecorder() *Recorder { 271 return &Recorder{make(map[devInode]Info), 0} 272 }