github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/cpio/fs_plan9.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 "log" 11 "os" 12 "path/filepath" 13 "syscall" 14 "time" 15 16 "github.com/mvdan/u-root-coreutils/pkg/ls" 17 "github.com/mvdan/u-root-coreutils/pkg/uio" 18 "github.com/mvdan/u-root-coreutils/pkg/upath" 19 ) 20 21 // A Recorder is a structure that contains variables used to calculate 22 // file parameters such as inode numbers for a CPIO file. The life-time 23 // of a Record structure is meant to be the same as the construction of a 24 // single CPIO archive. Do not reuse between CPIOs if you don't know what 25 // you're doing. 26 type Recorder struct { 27 inumber uint64 28 } 29 30 var modeMap = map[uint64]os.FileMode{ 31 modeFile: 0, 32 modeDir: os.ModeDir, 33 } 34 35 func unixModeToFileType(m uint64) (os.FileMode, error) { 36 if t, ok := modeMap[m&modeTypeMask]; ok { 37 return t, nil 38 } 39 return 0, fmt.Errorf("invalid file type %#o", m&modeTypeMask) 40 } 41 42 func toFileMode(r Record) os.FileMode { 43 return os.FileMode(perm(r)) 44 } 45 46 // setModes sets the modes. 47 func setModes(r Record) error { 48 if err := os.Chmod(r.Name, toFileMode(r)&os.ModePerm); err != nil { 49 return err 50 } 51 return nil 52 } 53 54 func perm(r Record) uint32 { 55 return uint32(r.Mode) & modePermissions 56 } 57 58 func dev(r Record) int { 59 return int(r.Rmajor<<8 | r.Rminor) 60 } 61 62 // CreateFile creates a local file for f relative to the current working 63 // directory. 64 // 65 // CreateFile will attempt to set all metadata for the file, including 66 // ownership, times, and permissions. 67 func CreateFile(f Record) error { 68 return CreateFileInRoot(f, ".", true) 69 } 70 71 // CreateFileInRoot creates a local file for f relative to rootDir. 72 func CreateFileInRoot(f Record, rootDir string, forcePriv bool) error { 73 m, err := unixModeToFileType(f.Mode) 74 if err != nil { 75 return err 76 } 77 78 f.Name, err = upath.SafeFilepathJoin(rootDir, f.Name) 79 if err != nil { 80 // The behavior is to skip files which are unsafe due to 81 // zipslip, but continue extracting everything else. 82 log.Printf("Warning: Skipping file %q due to: %v", f.Name, err) 83 return nil 84 } 85 dir := filepath.Dir(f.Name) 86 // The problem: many cpio archives do not specify the directories and 87 // hence the permissions. They just specify the whole path. In order 88 // to create files in these directories, we have to make them at least 89 // mode 755. 90 if _, err := os.Stat(dir); os.IsNotExist(err) && len(dir) > 0 { 91 if err := os.MkdirAll(dir, 0o755); err != nil { 92 return fmt.Errorf("CreateFileInRoot %q: %v", f.Name, err) 93 } 94 } 95 96 switch m { 97 case os.FileMode(0): 98 nf, err := os.Create(f.Name) 99 if err != nil { 100 return err 101 } 102 defer nf.Close() 103 if _, err := io.Copy(nf, uio.Reader(f)); err != nil { 104 return err 105 } 106 107 case os.ModeDir: 108 if err := os.MkdirAll(f.Name, toFileMode(f)); err != nil { 109 return err 110 } 111 112 default: 113 return fmt.Errorf("%v: Unknown type %#o", f.Name, m) 114 } 115 116 if err := setModes(f); err != nil && forcePriv { 117 return err 118 } 119 return nil 120 } 121 122 func (r *Recorder) inode(i Info) Info { 123 i.Ino = r.inumber 124 r.inumber++ 125 return i 126 } 127 128 // GetRecord returns a cpio Record for the given path on the local file system. 129 // 130 // GetRecord does not follow symlinks. If path is a symlink, the record 131 // returned will reflect that symlink. 132 func (r *Recorder) GetRecord(path string) (Record, error) { 133 fi, err := os.Stat(path) 134 if err != nil { 135 return Record{}, err 136 } 137 138 sys := fi.Sys().(*syscall.Dir) 139 info := r.inode(sysInfo(path, sys)) 140 141 switch fi.Mode() & os.ModeType { 142 case 0: // Regular file. 143 return Record{Info: info, ReaderAt: uio.NewLazyFile(path)}, nil 144 default: 145 return StaticRecord(nil, info), nil 146 } 147 } 148 149 // NewRecorder creates a new Recorder. 150 // 151 // A recorder is a structure that contains variables used to calculate 152 // file parameters such as inode numbers for a CPIO file. The life-time 153 // of a Record structure is meant to be the same as the construction of a 154 // single CPIO archive. Do not reuse between CPIOs if you don't know what 155 // you're doing. 156 func NewRecorder() *Recorder { 157 return &Recorder{inumber: 2} 158 } 159 160 // LSInfoFromRecord converts a Record to be usable with the ls package for 161 // listing files. 162 func LSInfoFromRecord(rec Record) ls.FileInfo { 163 mode := modeFromLinux(rec.Mode) 164 return ls.FileInfo{ 165 Name: rec.Name, 166 Mode: mode, 167 UID: fmt.Sprintf("%d", rec.UID), 168 Size: int64(rec.FileSize), 169 MTime: time.Unix(int64(rec.MTime), 0).UTC(), 170 } 171 }