You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

chroot.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package chroot
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strings"
  6. "github.com/go-git/go-billy/v5"
  7. "github.com/go-git/go-billy/v5/helper/polyfill"
  8. )
  9. // ChrootHelper is a helper to implement billy.Chroot.
  10. type ChrootHelper struct {
  11. underlying billy.Filesystem
  12. base string
  13. }
  14. // New creates a new filesystem wrapping up the given 'fs'.
  15. // The created filesystem has its base in the given ChrootHelperectory of the
  16. // underlying filesystem.
  17. func New(fs billy.Basic, base string) billy.Filesystem {
  18. return &ChrootHelper{
  19. underlying: polyfill.New(fs),
  20. base: base,
  21. }
  22. }
  23. func (fs *ChrootHelper) underlyingPath(filename string) (string, error) {
  24. if isCrossBoundaries(filename) {
  25. return "", billy.ErrCrossedBoundary
  26. }
  27. return fs.Join(fs.Root(), filename), nil
  28. }
  29. func isCrossBoundaries(path string) bool {
  30. path = filepath.ToSlash(path)
  31. path = filepath.Clean(path)
  32. return strings.HasPrefix(path, ".."+string(filepath.Separator))
  33. }
  34. func (fs *ChrootHelper) Create(filename string) (billy.File, error) {
  35. fullpath, err := fs.underlyingPath(filename)
  36. if err != nil {
  37. return nil, err
  38. }
  39. f, err := fs.underlying.Create(fullpath)
  40. if err != nil {
  41. return nil, err
  42. }
  43. return newFile(fs, f, filename), nil
  44. }
  45. func (fs *ChrootHelper) Open(filename string) (billy.File, error) {
  46. fullpath, err := fs.underlyingPath(filename)
  47. if err != nil {
  48. return nil, err
  49. }
  50. f, err := fs.underlying.Open(fullpath)
  51. if err != nil {
  52. return nil, err
  53. }
  54. return newFile(fs, f, filename), nil
  55. }
  56. func (fs *ChrootHelper) OpenFile(filename string, flag int, mode os.FileMode) (billy.File, error) {
  57. fullpath, err := fs.underlyingPath(filename)
  58. if err != nil {
  59. return nil, err
  60. }
  61. f, err := fs.underlying.OpenFile(fullpath, flag, mode)
  62. if err != nil {
  63. return nil, err
  64. }
  65. return newFile(fs, f, filename), nil
  66. }
  67. func (fs *ChrootHelper) Stat(filename string) (os.FileInfo, error) {
  68. fullpath, err := fs.underlyingPath(filename)
  69. if err != nil {
  70. return nil, err
  71. }
  72. return fs.underlying.Stat(fullpath)
  73. }
  74. func (fs *ChrootHelper) Rename(from, to string) error {
  75. var err error
  76. from, err = fs.underlyingPath(from)
  77. if err != nil {
  78. return err
  79. }
  80. to, err = fs.underlyingPath(to)
  81. if err != nil {
  82. return err
  83. }
  84. return fs.underlying.Rename(from, to)
  85. }
  86. func (fs *ChrootHelper) Remove(path string) error {
  87. fullpath, err := fs.underlyingPath(path)
  88. if err != nil {
  89. return err
  90. }
  91. return fs.underlying.Remove(fullpath)
  92. }
  93. func (fs *ChrootHelper) Join(elem ...string) string {
  94. return fs.underlying.Join(elem...)
  95. }
  96. func (fs *ChrootHelper) TempFile(dir, prefix string) (billy.File, error) {
  97. fullpath, err := fs.underlyingPath(dir)
  98. if err != nil {
  99. return nil, err
  100. }
  101. f, err := fs.underlying.(billy.TempFile).TempFile(fullpath, prefix)
  102. if err != nil {
  103. return nil, err
  104. }
  105. return newFile(fs, f, fs.Join(dir, filepath.Base(f.Name()))), nil
  106. }
  107. func (fs *ChrootHelper) ReadDir(path string) ([]os.FileInfo, error) {
  108. fullpath, err := fs.underlyingPath(path)
  109. if err != nil {
  110. return nil, err
  111. }
  112. return fs.underlying.(billy.Dir).ReadDir(fullpath)
  113. }
  114. func (fs *ChrootHelper) MkdirAll(filename string, perm os.FileMode) error {
  115. fullpath, err := fs.underlyingPath(filename)
  116. if err != nil {
  117. return err
  118. }
  119. return fs.underlying.(billy.Dir).MkdirAll(fullpath, perm)
  120. }
  121. func (fs *ChrootHelper) Lstat(filename string) (os.FileInfo, error) {
  122. fullpath, err := fs.underlyingPath(filename)
  123. if err != nil {
  124. return nil, err
  125. }
  126. return fs.underlying.(billy.Symlink).Lstat(fullpath)
  127. }
  128. func (fs *ChrootHelper) Symlink(target, link string) error {
  129. target = filepath.FromSlash(target)
  130. // only rewrite target if it's already absolute
  131. if filepath.IsAbs(target) || strings.HasPrefix(target, string(filepath.Separator)) {
  132. target = fs.Join(fs.Root(), target)
  133. target = filepath.Clean(filepath.FromSlash(target))
  134. }
  135. link, err := fs.underlyingPath(link)
  136. if err != nil {
  137. return err
  138. }
  139. return fs.underlying.(billy.Symlink).Symlink(target, link)
  140. }
  141. func (fs *ChrootHelper) Readlink(link string) (string, error) {
  142. fullpath, err := fs.underlyingPath(link)
  143. if err != nil {
  144. return "", err
  145. }
  146. target, err := fs.underlying.(billy.Symlink).Readlink(fullpath)
  147. if err != nil {
  148. return "", err
  149. }
  150. if !filepath.IsAbs(target) && !strings.HasPrefix(target, string(filepath.Separator)) {
  151. return target, nil
  152. }
  153. target, err = filepath.Rel(fs.base, target)
  154. if err != nil {
  155. return "", err
  156. }
  157. return string(os.PathSeparator) + target, nil
  158. }
  159. func (fs *ChrootHelper) Chroot(path string) (billy.Filesystem, error) {
  160. fullpath, err := fs.underlyingPath(path)
  161. if err != nil {
  162. return nil, err
  163. }
  164. return New(fs.underlying, fullpath), nil
  165. }
  166. func (fs *ChrootHelper) Root() string {
  167. return fs.base
  168. }
  169. func (fs *ChrootHelper) Underlying() billy.Basic {
  170. return fs.underlying
  171. }
  172. // Capabilities implements the Capable interface.
  173. func (fs *ChrootHelper) Capabilities() billy.Capability {
  174. return billy.Capabilities(fs.underlying)
  175. }
  176. type file struct {
  177. billy.File
  178. name string
  179. }
  180. func newFile(fs billy.Filesystem, f billy.File, filename string) billy.File {
  181. filename = fs.Join(fs.Root(), filename)
  182. filename, _ = filepath.Rel(fs.Root(), filename)
  183. return &file{
  184. File: f,
  185. name: filename,
  186. }
  187. }
  188. func (f *file) Name() string {
  189. return f.name
  190. }