filesystem.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package docker
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "syscall"
  11. "time"
  12. )
  13. type Filesystem struct {
  14. RootFS string
  15. RWPath string
  16. // The layers to be mounted on top of each other via aufs.
  17. // Layers are ordered top-to-bottom: the first layer in the list will be mounted on top of the others.
  18. // In other words, THE BASE IMAGE SHOULD BE LAST!
  19. Layers []string
  20. }
  21. func (fs *Filesystem) createMountPoints() error {
  22. if err := os.Mkdir(fs.RootFS, 0700); err != nil && !os.IsExist(err) {
  23. return err
  24. }
  25. if err := os.Mkdir(fs.RWPath, 0700); err != nil && !os.IsExist(err) {
  26. return err
  27. }
  28. return nil
  29. }
  30. func (fs *Filesystem) Mount() error {
  31. if fs.IsMounted() {
  32. return errors.New("Mount: Filesystem already mounted")
  33. }
  34. if err := fs.createMountPoints(); err != nil {
  35. return err
  36. }
  37. rwBranch := fmt.Sprintf("%v=rw", fs.RWPath)
  38. roBranches := ""
  39. for _, layer := range fs.Layers {
  40. roBranches += fmt.Sprintf("%v=ro:", layer)
  41. }
  42. branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
  43. if err := syscall.Mount("none", fs.RootFS, "aufs", 0, branches); err != nil {
  44. return err
  45. }
  46. if !fs.IsMounted() {
  47. return errors.New("Mount failed")
  48. }
  49. return nil
  50. }
  51. func (fs *Filesystem) Umount() error {
  52. if !fs.IsMounted() {
  53. return errors.New("Umount: Filesystem not mounted")
  54. }
  55. if err := syscall.Unmount(fs.RootFS, 0); err != nil {
  56. return err
  57. }
  58. if fs.IsMounted() {
  59. return fmt.Errorf("Umount: Filesystem still mounted after calling umount(%v)", fs.RootFS)
  60. }
  61. // Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
  62. // for some time. We'll just keep retrying until it succeeds.
  63. for retries := 0; retries < 1000; retries++ {
  64. err := os.Remove(fs.RootFS)
  65. if err == nil {
  66. // rm mntpoint succeeded
  67. return nil
  68. }
  69. if os.IsNotExist(err) {
  70. // mntpoint doesn't exist anymore. Success.
  71. return nil
  72. }
  73. // fmt.Printf("(%v) Remove %v returned: %v\n", retries, fs.RootFS, err)
  74. time.Sleep(10 * time.Millisecond)
  75. }
  76. return fmt.Errorf("Umount: Failed to umount %v", fs.RootFS)
  77. }
  78. func (fs *Filesystem) IsMounted() bool {
  79. mntpoint, err := os.Stat(fs.RootFS)
  80. if err != nil {
  81. if os.IsNotExist(err) {
  82. return false
  83. }
  84. panic(err)
  85. }
  86. parent, err := os.Stat(filepath.Join(fs.RootFS, ".."))
  87. if err != nil {
  88. panic(err)
  89. }
  90. mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
  91. parentSt := parent.Sys().(*syscall.Stat_t)
  92. return mntpointSt.Dev != parentSt.Dev
  93. }
  94. // Tar returns the contents of the filesystem as an uncompressed tar stream
  95. func (fs *Filesystem) Tar() (io.Reader, error) {
  96. if err := fs.EnsureMounted(); err != nil {
  97. return nil, err
  98. }
  99. return Tar(fs.RootFS)
  100. }
  101. func (fs *Filesystem) EnsureMounted() error {
  102. if !fs.IsMounted() {
  103. if err := fs.Mount(); err != nil {
  104. return err
  105. }
  106. }
  107. return nil
  108. }
  109. type ChangeType int
  110. const (
  111. ChangeModify = iota
  112. ChangeAdd
  113. ChangeDelete
  114. )
  115. type Change struct {
  116. Path string
  117. Kind ChangeType
  118. }
  119. func (change *Change) String() string {
  120. var kind string
  121. switch change.Kind {
  122. case ChangeModify:
  123. kind = "C"
  124. case ChangeAdd:
  125. kind = "A"
  126. case ChangeDelete:
  127. kind = "D"
  128. }
  129. return fmt.Sprintf("%s %s", kind, change.Path)
  130. }
  131. func (fs *Filesystem) Changes() ([]Change, error) {
  132. var changes []Change
  133. err := filepath.Walk(fs.RWPath, func(path string, f os.FileInfo, err error) error {
  134. if err != nil {
  135. return err
  136. }
  137. // Rebase path
  138. path, err = filepath.Rel(fs.RWPath, path)
  139. if err != nil {
  140. return err
  141. }
  142. path = filepath.Join("/", path)
  143. // Skip root
  144. if path == "/" {
  145. return nil
  146. }
  147. // Skip AUFS metadata
  148. if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
  149. return err
  150. }
  151. change := Change{
  152. Path: path,
  153. }
  154. // Find out what kind of modification happened
  155. file := filepath.Base(path)
  156. // If there is a whiteout, then the file was removed
  157. if strings.HasPrefix(file, ".wh.") {
  158. originalFile := strings.TrimLeft(file, ".wh.")
  159. change.Path = filepath.Join(filepath.Dir(path), originalFile)
  160. change.Kind = ChangeDelete
  161. } else {
  162. // Otherwise, the file was added
  163. change.Kind = ChangeAdd
  164. // ...Unless it already existed in a top layer, in which case, it's a modification
  165. for _, layer := range fs.Layers {
  166. stat, err := os.Stat(filepath.Join(layer, path))
  167. if err != nil && !os.IsNotExist(err) {
  168. return err
  169. }
  170. if err == nil {
  171. // The file existed in the top layer, so that's a modification
  172. // However, if it's a directory, maybe it wasn't actually modified.
  173. // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
  174. if stat.IsDir() && f.IsDir() {
  175. if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
  176. // Both directories are the same, don't record the change
  177. return nil
  178. }
  179. }
  180. change.Kind = ChangeModify
  181. break
  182. }
  183. }
  184. }
  185. // Record change
  186. changes = append(changes, change)
  187. return nil
  188. })
  189. if err != nil {
  190. return nil, err
  191. }
  192. return changes, nil
  193. }
  194. // Reset removes all changes to the filesystem, reverting it to its initial state.
  195. func (fs *Filesystem) Reset() error {
  196. if err := os.RemoveAll(fs.RWPath); err != nil {
  197. return err
  198. }
  199. // We removed the RW directory itself along with its content: let's re-create an empty one.
  200. if err := fs.createMountPoints(); err != nil {
  201. return err
  202. }
  203. return nil
  204. }
  205. // Open opens the named file for reading.
  206. func (fs *Filesystem) OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
  207. if err := fs.EnsureMounted(); err != nil {
  208. return nil, err
  209. }
  210. return os.OpenFile(filepath.Join(fs.RootFS, path), flag, perm)
  211. }
  212. // ReadDir reads the directory named by dirname, relative to the Filesystem's root,
  213. // and returns a list of sorted directory entries
  214. func (fs *Filesystem) ReadDir(dirname string) ([]os.FileInfo, error) {
  215. if err := fs.EnsureMounted(); err != nil {
  216. return nil, err
  217. }
  218. return ioutil.ReadDir(filepath.Join(fs.RootFS, dirname))
  219. }
  220. func newFilesystem(rootfs string, rwpath string, layers []string) *Filesystem {
  221. return &Filesystem{
  222. RootFS: rootfs,
  223. RWPath: rwpath,
  224. Layers: layers,
  225. }
  226. }