github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/tools/quickrm/main.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 main 16 17 import ( 18 "bufio" 19 "flag" 20 "fmt" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "sort" 25 "strconv" 26 "strings" 27 28 "github.com/hashicorp/errwrap" 29 "github.com/rkt/rkt/tools/common" 30 ) 31 32 // processor processes words from files generated by make 33 type processor interface { 34 kind() string 35 setWordsNumber(number int) 36 processWord(word string) error 37 getList() []string 38 remove(name string) error 39 } 40 41 // fileProcessor is the simplest from all processor 42 // implementations. Nothing special needs to be done for regular 43 // files, so it simply appends all the data passed to it. 44 type fileProcessor struct { 45 list []string 46 } 47 48 // symlinkProcessor prepares symlinks for removal - it makes sure that 49 // the directory part of a path to symlink has no symlinks in it. This 50 // is to make sure, that if a symlink is removed, then we can remove 51 // also other symlinks that were previously accessible via the just 52 // removed symlink. 53 type symlinkProcessor struct { 54 list []string 55 } 56 57 // dirProcessor, like symlinkProcessor, evaluates symlinks in paths it 58 // receives. It also sorts the directories, so the leaves are removed 59 // first. 60 type dirProcessor struct { 61 set map[string]struct{} 62 } 63 64 type items struct { 65 proc processor 66 list []string 67 } 68 69 type kindData struct { 70 filename string 71 option string 72 proc processor 73 } 74 75 func main() { 76 allItems := getItems() 77 78 failed := false 79 for _, i := range allItems { 80 for _, el := range i.list { 81 if err := i.proc.remove(el); err != nil { 82 if os.IsNotExist(err) { 83 continue 84 } 85 common.Warn("Failed to remove %s %q: %v", i.proc.kind(), el, err) 86 failed = true 87 if infos, err := ioutil.ReadDir(el); err == nil { 88 var contents []string 89 for _, fi := range infos { 90 contents = append(contents, fi.Name()) 91 } 92 common.Warn(" Contents of the directory: %s", strings.Join(contents, ", ")) 93 } 94 } 95 } 96 } 97 if failed { 98 os.Exit(1) 99 } 100 } 101 102 func getItems() []items { 103 kinds := []*kindData{ 104 { 105 option: "files", 106 proc: &fileProcessor{}, 107 }, 108 { 109 option: "symlinks", 110 proc: &symlinkProcessor{}, 111 }, 112 { 113 option: "dirs", 114 proc: &dirProcessor{}, 115 }, 116 } 117 118 for _, k := range kinds { 119 flag.StringVar(&k.filename, k.option, "", fmt.Sprintf("List of %s", k.option)) 120 } 121 flag.Parse() 122 for _, k := range kinds { 123 if k.filename == "" { 124 common.Die("No --%s parameter passed", k.option) 125 } 126 } 127 128 allItems := make([]items, 0, len(kinds)) 129 for _, k := range kinds { 130 l, err := getList(k.filename, k.proc) 131 if err != nil { 132 common.Die("Failed to get %s list: %v", k.proc.kind(), err) 133 } 134 allItems = append(allItems, items{ 135 proc: k.proc, 136 list: l, 137 }) 138 } 139 140 return allItems 141 } 142 143 func getList(file string, proc processor) ([]string, error) { 144 fd, err := os.Open(file) 145 if err != nil { 146 return nil, errwrap.Wrap(fmt.Errorf("failed to open %s list %q", proc.kind(), file), err) 147 } 148 defer fd.Close() 149 sc := bufio.NewScanner(fd) 150 sc.Split(bufio.ScanWords) 151 if !sc.Scan() { 152 if err := sc.Err(); err != nil { 153 return nil, errwrap.Wrap(fmt.Errorf("failed to read %s list %q", proc.kind(), file), err) 154 } 155 return nil, fmt.Errorf("failed to parse %s list %q: premature end of file", proc.kind(), file) 156 } 157 num, err := strconv.Atoi(sc.Text()) 158 if err != nil { 159 return nil, errwrap.Wrap(fmt.Errorf("failed to parse first line of %s list %q", proc.kind(), file), err) 160 } 161 proc.setWordsNumber(num) 162 for sc.Scan() { 163 if err := proc.processWord(sc.Text()); err != nil { 164 return nil, errwrap.Wrap(fmt.Errorf("failed to process %s from %s list %q", sc.Text(), proc.kind(), file), err) 165 } 166 } 167 if err := sc.Err(); err != nil { 168 return nil, errwrap.Wrap(fmt.Errorf("failed to read %s list %q", proc.kind(), file), err) 169 } 170 return proc.getList(), nil 171 } 172 173 // file processor 174 175 func (proc *fileProcessor) kind() string { 176 return "file" 177 } 178 179 func (proc *fileProcessor) remove(name string) error { 180 return os.Remove(name) 181 } 182 183 func (proc *fileProcessor) setWordsNumber(number int) { 184 proc.list = make([]string, 0, number) 185 } 186 187 func (proc *fileProcessor) processWord(file string) error { 188 if file != "" { 189 proc.list = append(proc.list, file) 190 } 191 return nil 192 } 193 194 func (proc *fileProcessor) getList() []string { 195 return proc.list 196 } 197 198 // symlink processor 199 200 func (proc *symlinkProcessor) kind() string { 201 return "symlink" 202 } 203 204 func (proc *symlinkProcessor) remove(name string) error { 205 return os.Remove(name) 206 } 207 208 func (proc *symlinkProcessor) setWordsNumber(number int) { 209 proc.list = make([]string, 0, number) 210 } 211 212 func (proc *symlinkProcessor) processWord(symlink string) error { 213 if symlink == "" { 214 return nil 215 } 216 symlinkDir := filepath.Dir(symlink) 217 symlinkBase := filepath.Base(symlink) 218 realSymlinkDir, err := filepath.EvalSymlinks(symlinkDir) 219 if err != nil { 220 if os.IsNotExist(err) { 221 return nil 222 } 223 return errwrap.Wrap(fmt.Errorf("failed to evaluate symlinks for symlink dir %q", symlinkDir), err) 224 } 225 proc.list = append(proc.list, filepath.Join(realSymlinkDir, symlinkBase)) 226 return nil 227 } 228 229 func (proc *symlinkProcessor) getList() []string { 230 return proc.list 231 } 232 233 // dir processor 234 235 func (proc *dirProcessor) kind() string { 236 return "directory" 237 } 238 239 func (proc *dirProcessor) remove(name string) error { 240 return os.RemoveAll(name) 241 } 242 243 func (proc *dirProcessor) setWordsNumber(number int) { 244 proc.set = make(map[string]struct{}, number) 245 } 246 247 func (proc *dirProcessor) processWord(dir string) error { 248 if dir == "" { 249 return nil 250 } 251 realDir, err := filepath.EvalSymlinks(dir) 252 if err != nil { 253 if os.IsNotExist(err) { 254 return nil 255 } 256 return errwrap.Wrap(fmt.Errorf("failed to evaluate symlinks for dir %q", dir), err) 257 } 258 proc.set[realDir] = struct{}{} 259 return nil 260 } 261 262 func (proc *dirProcessor) getList() []string { 263 list := make([]string, 0, len(proc.set)) 264 for d := range proc.set { 265 list = append(list, d) 266 } 267 sort.Sort(sort.Reverse(sort.StringSlice(list))) 268 return list 269 }