fuseoverlayfs.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. // +build linux
  2. package fuseoverlayfs // import "github.com/docker/docker/daemon/graphdriver/fuse-overlayfs"
  3. import (
  4. "bytes"
  5. "context"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "os"
  10. "os/exec"
  11. "path"
  12. "path/filepath"
  13. "strings"
  14. "github.com/containerd/containerd/sys"
  15. "github.com/docker/docker/daemon/graphdriver"
  16. "github.com/docker/docker/daemon/graphdriver/overlayutils"
  17. "github.com/docker/docker/pkg/archive"
  18. "github.com/docker/docker/pkg/chrootarchive"
  19. "github.com/docker/docker/pkg/containerfs"
  20. "github.com/docker/docker/pkg/directory"
  21. "github.com/docker/docker/pkg/idtools"
  22. "github.com/docker/docker/pkg/parsers/kernel"
  23. "github.com/docker/docker/pkg/system"
  24. "github.com/moby/locker"
  25. "github.com/moby/sys/mount"
  26. "github.com/opencontainers/selinux/go-selinux/label"
  27. "github.com/pkg/errors"
  28. "github.com/sirupsen/logrus"
  29. "golang.org/x/sys/unix"
  30. )
  31. var (
  32. // untar defines the untar method
  33. untar = chrootarchive.UntarUncompressed
  34. )
  35. const (
  36. driverName = "fuse-overlayfs"
  37. binary = "fuse-overlayfs"
  38. linkDir = "l"
  39. diffDirName = "diff"
  40. workDirName = "work"
  41. mergedDirName = "merged"
  42. lowerFile = "lower"
  43. maxDepth = 128
  44. // idLength represents the number of random characters
  45. // which can be used to create the unique link identifier
  46. // for every layer. If this value is too long then the
  47. // page size limit for the mount command may be exceeded.
  48. // The idLength should be selected such that following equation
  49. // is true (512 is a buffer for label metadata).
  50. // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512)
  51. idLength = 26
  52. )
  53. // Driver contains information about the home directory and the list of active
  54. // mounts that are created using this driver.
  55. type Driver struct {
  56. home string
  57. uidMaps []idtools.IDMap
  58. gidMaps []idtools.IDMap
  59. ctr *graphdriver.RefCounter
  60. naiveDiff graphdriver.DiffDriver
  61. locker *locker.Locker
  62. }
  63. var (
  64. logger = logrus.WithField("storage-driver", driverName)
  65. )
  66. func init() {
  67. graphdriver.Register(driverName, Init)
  68. }
  69. // Init returns the naive diff driver for fuse-overlayfs.
  70. // If fuse-overlayfs is not supported on the host, the error
  71. // graphdriver.ErrNotSupported is returned.
  72. func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
  73. if _, err := exec.LookPath(binary); err != nil {
  74. logger.Error(err)
  75. return nil, graphdriver.ErrNotSupported
  76. }
  77. if !kernel.CheckKernelVersion(4, 18, 0) {
  78. return nil, graphdriver.ErrNotSupported
  79. }
  80. remappedRoot := idtools.NewIDMappingsFromMaps(uidMaps, gidMaps)
  81. currentID := idtools.CurrentIdentity()
  82. dirID := idtools.Identity{
  83. UID: currentID.UID,
  84. GID: remappedRoot.RootPair().GID,
  85. }
  86. if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil {
  87. return nil, err
  88. }
  89. if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 700, currentID); err != nil {
  90. return nil, err
  91. }
  92. d := &Driver{
  93. home: home,
  94. uidMaps: uidMaps,
  95. gidMaps: gidMaps,
  96. ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicFUSE)),
  97. locker: locker.New(),
  98. }
  99. d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps)
  100. return d, nil
  101. }
  102. func (d *Driver) String() string {
  103. return driverName
  104. }
  105. // Status returns current driver information in a two dimensional string array.
  106. func (d *Driver) Status() [][2]string {
  107. return [][2]string{}
  108. }
  109. // GetMetadata returns metadata about the overlay driver such as the LowerDir,
  110. // UpperDir, WorkDir, and MergeDir used to store data.
  111. func (d *Driver) GetMetadata(id string) (map[string]string, error) {
  112. dir := d.dir(id)
  113. if _, err := os.Stat(dir); err != nil {
  114. return nil, err
  115. }
  116. metadata := map[string]string{
  117. "WorkDir": path.Join(dir, workDirName),
  118. "MergedDir": path.Join(dir, mergedDirName),
  119. "UpperDir": path.Join(dir, diffDirName),
  120. }
  121. lowerDirs, err := d.getLowerDirs(id)
  122. if err != nil {
  123. return nil, err
  124. }
  125. if len(lowerDirs) > 0 {
  126. metadata["LowerDir"] = strings.Join(lowerDirs, ":")
  127. }
  128. return metadata, nil
  129. }
  130. // Cleanup any state created by overlay which should be cleaned when daemon
  131. // is being shutdown. For now, we just have to unmount the bind mounted
  132. // we had created.
  133. func (d *Driver) Cleanup() error {
  134. return mount.RecursiveUnmount(d.home)
  135. }
  136. // CreateReadWrite creates a layer that is writable for use as a container
  137. // file system.
  138. func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
  139. if opts != nil && len(opts.StorageOpt) != 0 {
  140. return fmt.Errorf("--storage-opt is not supported")
  141. }
  142. return d.create(id, parent, opts)
  143. }
  144. // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
  145. // The parent filesystem is used to configure these directories for the overlay.
  146. func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) {
  147. if opts != nil && len(opts.StorageOpt) != 0 {
  148. return fmt.Errorf("--storage-opt is not supported")
  149. }
  150. return d.create(id, parent, opts)
  151. }
  152. func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) {
  153. dir := d.dir(id)
  154. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
  155. if err != nil {
  156. return err
  157. }
  158. root := idtools.Identity{UID: rootUID, GID: rootGID}
  159. dirID := idtools.Identity{
  160. UID: rootUID,
  161. GID: rootGID,
  162. }
  163. if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, dirID); err != nil {
  164. return err
  165. }
  166. if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil {
  167. return err
  168. }
  169. defer func() {
  170. // Clean up on failure
  171. if retErr != nil {
  172. os.RemoveAll(dir)
  173. }
  174. }()
  175. if opts != nil && len(opts.StorageOpt) > 0 {
  176. return fmt.Errorf("--storage-opt is not supported")
  177. }
  178. if err := idtools.MkdirAndChown(path.Join(dir, diffDirName), 0755, root); err != nil {
  179. return err
  180. }
  181. lid := overlayutils.GenerateID(idLength, logger)
  182. if err := os.Symlink(path.Join("..", id, diffDirName), path.Join(d.home, linkDir, lid)); err != nil {
  183. return err
  184. }
  185. // Write link id to link file
  186. if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil {
  187. return err
  188. }
  189. // if no parent directory, done
  190. if parent == "" {
  191. return nil
  192. }
  193. if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0710, dirID); err != nil {
  194. return err
  195. }
  196. if err := ioutil.WriteFile(path.Join(d.dir(parent), "committed"), []byte{}, 0600); err != nil {
  197. return err
  198. }
  199. lower, err := d.getLower(parent)
  200. if err != nil {
  201. return err
  202. }
  203. if lower != "" {
  204. if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil {
  205. return err
  206. }
  207. }
  208. return nil
  209. }
  210. func (d *Driver) getLower(parent string) (string, error) {
  211. parentDir := d.dir(parent)
  212. // Ensure parent exists
  213. if _, err := os.Lstat(parentDir); err != nil {
  214. return "", err
  215. }
  216. // Read Parent link fileA
  217. parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link"))
  218. if err != nil {
  219. return "", err
  220. }
  221. lowers := []string{path.Join(linkDir, string(parentLink))}
  222. parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile))
  223. if err == nil {
  224. parentLowers := strings.Split(string(parentLower), ":")
  225. lowers = append(lowers, parentLowers...)
  226. }
  227. if len(lowers) > maxDepth {
  228. return "", errors.New("max depth exceeded")
  229. }
  230. return strings.Join(lowers, ":"), nil
  231. }
  232. func (d *Driver) dir(id string) string {
  233. return path.Join(d.home, id)
  234. }
  235. func (d *Driver) getLowerDirs(id string) ([]string, error) {
  236. var lowersArray []string
  237. lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile))
  238. if err == nil {
  239. for _, s := range strings.Split(string(lowers), ":") {
  240. lp, err := os.Readlink(path.Join(d.home, s))
  241. if err != nil {
  242. return nil, err
  243. }
  244. lowersArray = append(lowersArray, path.Clean(path.Join(d.home, linkDir, lp)))
  245. }
  246. } else if !os.IsNotExist(err) {
  247. return nil, err
  248. }
  249. return lowersArray, nil
  250. }
  251. // Remove cleans the directories that are created for this id.
  252. func (d *Driver) Remove(id string) error {
  253. if id == "" {
  254. return fmt.Errorf("refusing to remove the directories: id is empty")
  255. }
  256. d.locker.Lock(id)
  257. defer d.locker.Unlock(id)
  258. dir := d.dir(id)
  259. lid, err := ioutil.ReadFile(path.Join(dir, "link"))
  260. if err == nil {
  261. if len(lid) == 0 {
  262. logger.Errorf("refusing to remove empty link for layer %v", id)
  263. } else if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil {
  264. logger.Debugf("Failed to remove link: %v", err)
  265. }
  266. }
  267. if err := system.EnsureRemoveAll(dir); err != nil && !os.IsNotExist(err) {
  268. return err
  269. }
  270. return nil
  271. }
  272. // Get creates and mounts the required file system for the given id and returns the mount path.
  273. func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr error) {
  274. d.locker.Lock(id)
  275. defer d.locker.Unlock(id)
  276. dir := d.dir(id)
  277. if _, err := os.Stat(dir); err != nil {
  278. return nil, err
  279. }
  280. diffDir := path.Join(dir, diffDirName)
  281. lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile))
  282. if err != nil {
  283. // If no lower, just return diff directory
  284. if os.IsNotExist(err) {
  285. return containerfs.NewLocalContainerFS(diffDir), nil
  286. }
  287. return nil, err
  288. }
  289. mergedDir := path.Join(dir, mergedDirName)
  290. if count := d.ctr.Increment(mergedDir); count > 1 {
  291. return containerfs.NewLocalContainerFS(mergedDir), nil
  292. }
  293. defer func() {
  294. if retErr != nil {
  295. if c := d.ctr.Decrement(mergedDir); c <= 0 {
  296. if unmounted := fusermountU(mergedDir); !unmounted {
  297. if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil {
  298. logger.Errorf("error unmounting %v: %v", mergedDir, mntErr)
  299. }
  300. }
  301. // Cleanup the created merged directory; see the comment in Put's rmdir
  302. if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) {
  303. logger.Debugf("Failed to remove %s: %v: %v", id, rmErr, err)
  304. }
  305. }
  306. }
  307. }()
  308. workDir := path.Join(dir, workDirName)
  309. splitLowers := strings.Split(string(lowers), ":")
  310. absLowers := make([]string, len(splitLowers))
  311. for i, s := range splitLowers {
  312. absLowers[i] = path.Join(d.home, s)
  313. }
  314. var readonly bool
  315. if _, err := os.Stat(path.Join(dir, "committed")); err == nil {
  316. readonly = true
  317. } else if !os.IsNotExist(err) {
  318. return nil, err
  319. }
  320. var opts string
  321. if readonly {
  322. opts = "lowerdir=" + diffDir + ":" + strings.Join(absLowers, ":")
  323. } else {
  324. opts = "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir
  325. }
  326. mountData := label.FormatMountLabel(opts, mountLabel)
  327. mountTarget := mergedDir
  328. rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
  329. if err != nil {
  330. return nil, err
  331. }
  332. if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil {
  333. return nil, err
  334. }
  335. mountProgram := exec.Command(binary, "-o", mountData, mountTarget)
  336. mountProgram.Dir = d.home
  337. var b bytes.Buffer
  338. mountProgram.Stderr = &b
  339. if err = mountProgram.Run(); err != nil {
  340. output := b.String()
  341. if output == "" {
  342. output = "<stderr empty>"
  343. }
  344. return nil, errors.Wrapf(err, "using mount program %s: %s", binary, output)
  345. }
  346. return containerfs.NewLocalContainerFS(mergedDir), nil
  347. }
  348. // Put unmounts the mount path created for the give id.
  349. // It also removes the 'merged' directory to force the kernel to unmount the
  350. // overlay mount in other namespaces.
  351. func (d *Driver) Put(id string) error {
  352. d.locker.Lock(id)
  353. defer d.locker.Unlock(id)
  354. dir := d.dir(id)
  355. _, err := ioutil.ReadFile(path.Join(dir, lowerFile))
  356. if err != nil {
  357. // If no lower, no mount happened and just return directly
  358. if os.IsNotExist(err) {
  359. return nil
  360. }
  361. return err
  362. }
  363. mountpoint := path.Join(dir, mergedDirName)
  364. if count := d.ctr.Decrement(mountpoint); count > 0 {
  365. return nil
  366. }
  367. if unmounted := fusermountU(mountpoint); !unmounted {
  368. if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil {
  369. logger.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err)
  370. }
  371. }
  372. // Remove the mountpoint here. Removing the mountpoint (in newer kernels)
  373. // will cause all other instances of this mount in other mount namespaces
  374. // to be unmounted. This is necessary to avoid cases where an overlay mount
  375. // that is present in another namespace will cause subsequent mounts
  376. // operations to fail with ebusy. We ignore any errors here because this may
  377. // fail on older kernels which don't have
  378. // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied.
  379. if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) {
  380. logger.Debugf("Failed to remove %s overlay: %v", id, err)
  381. }
  382. return nil
  383. }
  384. // Exists checks to see if the id is already mounted.
  385. func (d *Driver) Exists(id string) bool {
  386. _, err := os.Stat(d.dir(id))
  387. return err == nil
  388. }
  389. // isParent determines whether the given parent is the direct parent of the
  390. // given layer id
  391. func (d *Driver) isParent(id, parent string) bool {
  392. lowers, err := d.getLowerDirs(id)
  393. if err != nil {
  394. return false
  395. }
  396. if parent == "" && len(lowers) > 0 {
  397. return false
  398. }
  399. parentDir := d.dir(parent)
  400. var ld string
  401. if len(lowers) > 0 {
  402. ld = filepath.Dir(lowers[0])
  403. }
  404. if ld == "" && parent == "" {
  405. return true
  406. }
  407. return ld == parentDir
  408. }
  409. // ApplyDiff applies the new layer into a root
  410. func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) {
  411. if !d.isParent(id, parent) {
  412. return d.naiveDiff.ApplyDiff(id, parent, diff)
  413. }
  414. applyDir := d.getDiffPath(id)
  415. logger.Debugf("Applying tar in %s", applyDir)
  416. // Overlay doesn't need the parent id to apply the diff
  417. if err := untar(diff, applyDir, &archive.TarOptions{
  418. UIDMaps: d.uidMaps,
  419. GIDMaps: d.gidMaps,
  420. // Use AUFS whiteout format: https://github.com/containers/storage/blob/39a8d5ed9843844eafb5d2ba6e6a7510e0126f40/drivers/overlay/overlay.go#L1084-L1089
  421. WhiteoutFormat: archive.AUFSWhiteoutFormat,
  422. InUserNS: sys.RunningInUserNS(),
  423. }); err != nil {
  424. return 0, err
  425. }
  426. return directory.Size(context.TODO(), applyDir)
  427. }
  428. func (d *Driver) getDiffPath(id string) string {
  429. dir := d.dir(id)
  430. return path.Join(dir, diffDirName)
  431. }
  432. // DiffSize calculates the changes between the specified id
  433. // and its parent and returns the size in bytes of the changes
  434. // relative to its base filesystem directory.
  435. func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
  436. return d.naiveDiff.DiffSize(id, parent)
  437. }
  438. // Diff produces an archive of the changes between the specified
  439. // layer and its parent layer which may be "".
  440. func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) {
  441. return d.naiveDiff.Diff(id, parent)
  442. }
  443. // Changes produces a list of changes between the specified layer and its
  444. // parent layer. If parent is "", then all changes will be ADD changes.
  445. func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
  446. return d.naiveDiff.Changes(id, parent)
  447. }
  448. // fusermountU is from https://github.com/containers/storage/blob/39a8d5ed9843844eafb5d2ba6e6a7510e0126f40/drivers/overlay/overlay.go#L1016-L1040
  449. func fusermountU(mountpoint string) (unmounted bool) {
  450. // Attempt to unmount the FUSE mount using either fusermount or fusermount3.
  451. // If they fail, fallback to unix.Unmount
  452. for _, v := range []string{"fusermount3", "fusermount"} {
  453. err := exec.Command(v, "-u", mountpoint).Run()
  454. if err != nil && !os.IsNotExist(err) {
  455. logrus.Debugf("Error unmounting %s with %s - %v", mountpoint, v, err)
  456. }
  457. if err == nil {
  458. unmounted = true
  459. break
  460. }
  461. }
  462. // If fusermount|fusermount3 failed to unmount the FUSE file system, make sure all
  463. // pending changes are propagated to the file system
  464. if !unmounted {
  465. fd, err := unix.Open(mountpoint, unix.O_DIRECTORY, 0)
  466. if err == nil {
  467. if err := unix.Syncfs(fd); err != nil {
  468. logrus.Debugf("Error Syncfs(%s) - %v", mountpoint, err)
  469. }
  470. unix.Close(fd)
  471. }
  472. }
  473. return
  474. }