changes_linux.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. package archive
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "sort"
  8. "syscall"
  9. "unsafe"
  10. "github.com/docker/docker/pkg/system"
  11. "golang.org/x/sys/unix"
  12. )
  13. // walker is used to implement collectFileInfoForChanges on linux. Where this
  14. // method in general returns the entire contents of two directory trees, we
  15. // optimize some FS calls out on linux. In particular, we take advantage of the
  16. // fact that getdents(2) returns the inode of each file in the directory being
  17. // walked, which, when walking two trees in parallel to generate a list of
  18. // changes, can be used to prune subtrees without ever having to lstat(2) them
  19. // directly. Eliminating stat calls in this way can save up to seconds on large
  20. // images.
  21. type walker struct {
  22. dir1 string
  23. dir2 string
  24. root1 *FileInfo
  25. root2 *FileInfo
  26. }
  27. // collectFileInfoForChanges returns a complete representation of the trees
  28. // rooted at dir1 and dir2, with one important exception: any subtree or
  29. // leaf where the inode and device numbers are an exact match between dir1
  30. // and dir2 will be pruned from the results. This method is *only* to be used
  31. // to generating a list of changes between the two directories, as it does not
  32. // reflect the full contents.
  33. func collectFileInfoForChanges(dir1, dir2 string) (*FileInfo, *FileInfo, error) {
  34. w := &walker{
  35. dir1: dir1,
  36. dir2: dir2,
  37. root1: newRootFileInfo(),
  38. root2: newRootFileInfo(),
  39. }
  40. i1, err := os.Lstat(w.dir1)
  41. if err != nil {
  42. return nil, nil, err
  43. }
  44. i2, err := os.Lstat(w.dir2)
  45. if err != nil {
  46. return nil, nil, err
  47. }
  48. if err := w.walk("/", i1, i2); err != nil {
  49. return nil, nil, err
  50. }
  51. return w.root1, w.root2, nil
  52. }
  53. // Given a FileInfo, its path info, and a reference to the root of the tree
  54. // being constructed, register this file with the tree.
  55. func walkchunk(path string, fi os.FileInfo, dir string, root *FileInfo) error {
  56. if fi == nil {
  57. return nil
  58. }
  59. parent := root.LookUp(filepath.Dir(path))
  60. if parent == nil {
  61. return fmt.Errorf("walkchunk: Unexpectedly no parent for %s", path)
  62. }
  63. info := &FileInfo{
  64. name: filepath.Base(path),
  65. children: make(map[string]*FileInfo),
  66. parent: parent,
  67. }
  68. cpath := filepath.Join(dir, path)
  69. stat, err := system.FromStatT(fi.Sys().(*syscall.Stat_t))
  70. if err != nil {
  71. return err
  72. }
  73. info.stat = stat
  74. info.capability, _ = system.Lgetxattr(cpath, "security.capability") // lgetxattr(2): fs access
  75. parent.children[info.name] = info
  76. return nil
  77. }
  78. // Walk a subtree rooted at the same path in both trees being iterated. For
  79. // example, /docker/overlay/1234/a/b/c/d and /docker/overlay/8888/a/b/c/d
  80. func (w *walker) walk(path string, i1, i2 os.FileInfo) (err error) {
  81. // Register these nodes with the return trees, unless we're still at the
  82. // (already-created) roots:
  83. if path != "/" {
  84. if err := walkchunk(path, i1, w.dir1, w.root1); err != nil {
  85. return err
  86. }
  87. if err := walkchunk(path, i2, w.dir2, w.root2); err != nil {
  88. return err
  89. }
  90. }
  91. is1Dir := i1 != nil && i1.IsDir()
  92. is2Dir := i2 != nil && i2.IsDir()
  93. sameDevice := false
  94. if i1 != nil && i2 != nil {
  95. si1 := i1.Sys().(*syscall.Stat_t)
  96. si2 := i2.Sys().(*syscall.Stat_t)
  97. if si1.Dev == si2.Dev {
  98. sameDevice = true
  99. }
  100. }
  101. // If these files are both non-existent, or leaves (non-dirs), we are done.
  102. if !is1Dir && !is2Dir {
  103. return nil
  104. }
  105. // Fetch the names of all the files contained in both directories being walked:
  106. var names1, names2 []nameIno
  107. if is1Dir {
  108. names1, err = readdirnames(filepath.Join(w.dir1, path)) // getdents(2): fs access
  109. if err != nil {
  110. return err
  111. }
  112. }
  113. if is2Dir {
  114. names2, err = readdirnames(filepath.Join(w.dir2, path)) // getdents(2): fs access
  115. if err != nil {
  116. return err
  117. }
  118. }
  119. // We have lists of the files contained in both parallel directories, sorted
  120. // in the same order. Walk them in parallel, generating a unique merged list
  121. // of all items present in either or both directories.
  122. var names []string
  123. ix1 := 0
  124. ix2 := 0
  125. for {
  126. if ix1 >= len(names1) {
  127. break
  128. }
  129. if ix2 >= len(names2) {
  130. break
  131. }
  132. ni1 := names1[ix1]
  133. ni2 := names2[ix2]
  134. switch bytes.Compare([]byte(ni1.name), []byte(ni2.name)) {
  135. case -1: // ni1 < ni2 -- advance ni1
  136. // we will not encounter ni1 in names2
  137. names = append(names, ni1.name)
  138. ix1++
  139. case 0: // ni1 == ni2
  140. if ni1.ino != ni2.ino || !sameDevice {
  141. names = append(names, ni1.name)
  142. }
  143. ix1++
  144. ix2++
  145. case 1: // ni1 > ni2 -- advance ni2
  146. // we will not encounter ni2 in names1
  147. names = append(names, ni2.name)
  148. ix2++
  149. }
  150. }
  151. for ix1 < len(names1) {
  152. names = append(names, names1[ix1].name)
  153. ix1++
  154. }
  155. for ix2 < len(names2) {
  156. names = append(names, names2[ix2].name)
  157. ix2++
  158. }
  159. // For each of the names present in either or both of the directories being
  160. // iterated, stat the name under each root, and recurse the pair of them:
  161. for _, name := range names {
  162. fname := filepath.Join(path, name)
  163. var cInfo1, cInfo2 os.FileInfo
  164. if is1Dir {
  165. cInfo1, err = os.Lstat(filepath.Join(w.dir1, fname)) // lstat(2): fs access
  166. if err != nil && !os.IsNotExist(err) {
  167. return err
  168. }
  169. }
  170. if is2Dir {
  171. cInfo2, err = os.Lstat(filepath.Join(w.dir2, fname)) // lstat(2): fs access
  172. if err != nil && !os.IsNotExist(err) {
  173. return err
  174. }
  175. }
  176. if err = w.walk(fname, cInfo1, cInfo2); err != nil {
  177. return err
  178. }
  179. }
  180. return nil
  181. }
  182. // {name,inode} pairs used to support the early-pruning logic of the walker type
  183. type nameIno struct {
  184. name string
  185. ino uint64
  186. }
  187. type nameInoSlice []nameIno
  188. func (s nameInoSlice) Len() int { return len(s) }
  189. func (s nameInoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  190. func (s nameInoSlice) Less(i, j int) bool { return s[i].name < s[j].name }
  191. // readdirnames is a hacked-apart version of the Go stdlib code, exposing inode
  192. // numbers further up the stack when reading directory contents. Unlike
  193. // os.Readdirnames, which returns a list of filenames, this function returns a
  194. // list of {filename,inode} pairs.
  195. func readdirnames(dirname string) (names []nameIno, err error) {
  196. var (
  197. size = 100
  198. buf = make([]byte, 4096)
  199. nbuf int
  200. bufp int
  201. nb int
  202. )
  203. f, err := os.Open(dirname)
  204. if err != nil {
  205. return nil, err
  206. }
  207. defer f.Close()
  208. names = make([]nameIno, 0, size) // Empty with room to grow.
  209. for {
  210. // Refill the buffer if necessary
  211. if bufp >= nbuf {
  212. bufp = 0
  213. nbuf, err = unix.ReadDirent(int(f.Fd()), buf) // getdents on linux
  214. if nbuf < 0 {
  215. nbuf = 0
  216. }
  217. if err != nil {
  218. return nil, os.NewSyscallError("readdirent", err)
  219. }
  220. if nbuf <= 0 {
  221. break // EOF
  222. }
  223. }
  224. // Drain the buffer
  225. nb, names = parseDirent(buf[bufp:nbuf], names)
  226. bufp += nb
  227. }
  228. sl := nameInoSlice(names)
  229. sort.Sort(sl)
  230. return sl, nil
  231. }
  232. // parseDirent is a minor modification of unix.ParseDirent (linux version)
  233. // which returns {name,inode} pairs instead of just names.
  234. func parseDirent(buf []byte, names []nameIno) (consumed int, newnames []nameIno) {
  235. origlen := len(buf)
  236. for len(buf) > 0 {
  237. dirent := (*unix.Dirent)(unsafe.Pointer(&buf[0]))
  238. buf = buf[dirent.Reclen:]
  239. if dirent.Ino == 0 { // File absent in directory.
  240. continue
  241. }
  242. bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
  243. var name = string(bytes[0:clen(bytes[:])])
  244. if name == "." || name == ".." { // Useless names
  245. continue
  246. }
  247. names = append(names, nameIno{name, dirent.Ino})
  248. }
  249. return origlen - len(buf), names
  250. }
  251. func clen(n []byte) int {
  252. for i := 0; i < len(n); i++ {
  253. if n[i] == 0 {
  254. return i
  255. }
  256. }
  257. return len(n)
  258. }
  259. // OverlayChanges walks the path rw and determines changes for the files in the path,
  260. // with respect to the parent layers
  261. func OverlayChanges(layers []string, rw string) ([]Change, error) {
  262. return changes(layers, rw, overlayDeletedFile, nil)
  263. }
  264. func overlayDeletedFile(root, path string, fi os.FileInfo) (string, error) {
  265. if fi.Mode()&os.ModeCharDevice != 0 {
  266. s := fi.Sys().(*syscall.Stat_t)
  267. if major(uint64(s.Rdev)) == 0 && minor(uint64(s.Rdev)) == 0 {
  268. return path, nil
  269. }
  270. }
  271. if fi.Mode()&os.ModeDir != 0 {
  272. opaque, err := system.Lgetxattr(filepath.Join(root, path), "trusted.overlay.opaque")
  273. if err != nil {
  274. return "", err
  275. }
  276. if len(opaque) == 1 && opaque[0] == 'y' {
  277. return path, nil
  278. }
  279. }
  280. return "", nil
  281. }