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