logo

go-deblob

remove binary blobs from a directory git clone https://hacktivis.me/git/go-deblob.git

go-deblob.go (2885B)


  1. // This file is part of https://hacktivis.me/git/go-deblob
  2. // Copyright © 2019 Haelwenn (lanodan) Monnier <contact+go-deblob-notice@hacktivis.me>
  3. // SPDX-License-Identifier: BSD-3-Clause
  4. package main
  5. import (
  6. "bytes"
  7. "fmt"
  8. "git.sr.ht/~sircmpwn/getopt"
  9. "io"
  10. "log"
  11. "os"
  12. "path"
  13. )
  14. var excludes []string
  15. var noop bool
  16. func isBlob(filename string) (result bool) {
  17. result = false
  18. // 512 is larger than needed but it's one physical block size
  19. header := make([]byte, 512)
  20. file, err := os.Open(filename)
  21. if err != nil {
  22. log.Printf("os.Open(%s): %s\n", filename, err)
  23. return
  24. }
  25. defer file.Close()
  26. _, err = file.Read(header)
  27. if err != nil && err != io.EOF {
  28. log.Printf("(%s) os.Read: %s\n", filename, err)
  29. return
  30. }
  31. if bytes.HasPrefix(header, []byte("\x7fELF")) || bytes.HasPrefix(header, []byte("!<arch>\n")) || bytes.HasPrefix(header, []byte("\x55\xAA")) {
  32. result = true
  33. }
  34. return
  35. }
  36. func is_excluded(filename string) bool {
  37. for _, exclude := range excludes {
  38. matched, err := path.Match(exclude, filename)
  39. if err != nil {
  40. log.Printf("path.Match(%s, %s): %s\n", exclude, filename, err)
  41. return false
  42. }
  43. if matched {
  44. return true
  45. }
  46. }
  47. return false
  48. }
  49. func checkDir(dirname string, ignoring bool) {
  50. dirFd, err := os.Open(dirname)
  51. if err != nil {
  52. log.Printf("os.Open(%s): %s\n", dirname, err)
  53. return
  54. }
  55. files, err := dirFd.Readdir(0)
  56. if err != nil {
  57. log.Printf("(%s) os.Readdir: %v\n", dirname, err)
  58. return
  59. }
  60. for _, file := range files {
  61. filename := path.Join(dirname, file.Name())
  62. if !ignoring {
  63. ignoring = is_excluded(filename)
  64. }
  65. if file.IsDir() {
  66. checkDir(filename, ignoring)
  67. } else if isBlob(filename) {
  68. if ignoring {
  69. fmt.Printf("ignoring: %s\n", filename)
  70. continue
  71. }
  72. if noop {
  73. fmt.Printf("detected: %s\n", filename)
  74. continue
  75. }
  76. fmt.Printf("removing: %s\n", filename)
  77. err = os.Remove(filename)
  78. if err != nil {
  79. log.Printf("os.Remove(%s): %s\n", filename, err)
  80. }
  81. }
  82. }
  83. }
  84. func usage() {
  85. println("Usage: go-deblob [-e exclude] [-d workdir]")
  86. println(" -e Exclude filepath from removal (defaults to none)")
  87. println(" -d Set working directory (defaults to current dir)")
  88. println(" -n No actual removal, only scan and log")
  89. println("See `man 1 go-deblob` for more information.")
  90. }
  91. func main() {
  92. log.SetFlags(log.Lshortfile)
  93. workdir := "."
  94. opts, _, err := getopt.Getopts(os.Args, "e:d:n")
  95. if err != nil {
  96. log.Fatal(err)
  97. }
  98. for _, opt := range opts {
  99. switch opt.Option {
  100. case 'e':
  101. excludes = append(excludes, string(opt.Value))
  102. case 'd':
  103. workdir = opt.Value
  104. case 'n':
  105. noop = true
  106. default:
  107. usage()
  108. os.Exit(1)
  109. }
  110. }
  111. err = os.Chdir(workdir)
  112. if err != nil {
  113. log.Printf("os.Chdir(%s): %s\n", workdir, err)
  114. os.Exit(1)
  115. }
  116. fmt.Println(":: Checking for blobs")
  117. checkDir(".", false)
  118. fmt.Println(":: Done checking for blobs")
  119. }