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