filesystem.go 6.1 KB

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