2014-08-19 11:23:55 +02:00
// +build linux
2018-02-05 16:05:59 -05:00
package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay"
2014-08-19 11:23:55 +02:00
import (
"bufio"
"fmt"
2016-10-20 16:40:59 -07:00
"io"
2014-08-19 11:23:55 +02:00
"io/ioutil"
"os"
"os/exec"
"path"
2017-12-04 14:45:26 -08:00
"path/filepath"
2016-10-17 04:30:16 +00:00
"strconv"
2018-05-11 13:40:46 +02:00
"strings"
2014-08-19 11:23:55 +02:00
"github.com/docker/docker/daemon/graphdriver"
2017-08-29 09:01:23 -07:00
"github.com/docker/docker/daemon/graphdriver/copy"
2016-10-17 04:30:16 +00:00
"github.com/docker/docker/daemon/graphdriver/overlayutils"
2014-08-19 11:23:55 +02:00
"github.com/docker/docker/pkg/archive"
2017-08-03 17:22:00 -07:00
"github.com/docker/docker/pkg/containerfs"
2016-10-17 04:30:16 +00:00
"github.com/docker/docker/pkg/fsutils"
2015-10-08 11:51:41 -04:00
"github.com/docker/docker/pkg/idtools"
2017-02-17 15:46:19 -08:00
"github.com/docker/docker/pkg/locker"
2016-04-18 21:48:09 +00:00
"github.com/docker/docker/pkg/mount"
2018-05-11 13:40:46 +02:00
"github.com/docker/docker/pkg/parsers"
2017-02-14 13:35:20 -05:00
"github.com/docker/docker/pkg/system"
2017-04-18 15:26:36 +02:00
"github.com/opencontainers/selinux/go-selinux/label"
2017-07-26 14:42:13 -07:00
"github.com/sirupsen/logrus"
2017-05-23 10:22:32 -04:00
"golang.org/x/sys/unix"
2014-08-19 11:23:55 +02:00
)
// This is a small wrapper over the NaiveDiffWriter that lets us have a custom
// implementation of ApplyDiff()
var (
2015-07-24 17:55:15 +00:00
// ErrApplyDiffFallback is returned to indicate that a normal ApplyDiff is applied as a fallback from Naive diff writer.
2014-08-19 11:23:55 +02:00
ErrApplyDiffFallback = fmt . Errorf ( "Fall back to normal ApplyDiff" )
2016-05-10 15:25:54 +02:00
backingFs = "<unknown>"
2014-08-19 11:23:55 +02:00
)
2015-12-13 18:00:39 +02:00
// ApplyDiffProtoDriver wraps the ProtoDriver by extending the interface with ApplyDiff method.
2014-08-19 11:23:55 +02:00
type ApplyDiffProtoDriver interface {
graphdriver . ProtoDriver
2015-07-24 17:55:15 +00:00
// ApplyDiff writes the diff to the archive for the given id and parent id.
// It returns the size in bytes written if successful, an error ErrApplyDiffFallback is returned otherwise.
2016-10-20 16:40:59 -07:00
ApplyDiff ( id , parent string , diff io . Reader ) ( size int64 , err error )
2014-08-19 11:23:55 +02:00
}
type naiveDiffDriverWithApply struct {
graphdriver . Driver
applyDiff ApplyDiffProtoDriver
}
2015-07-24 17:55:15 +00:00
// NaiveDiffDriverWithApply returns a NaiveDiff driver with custom ApplyDiff.
2015-10-08 11:51:41 -04:00
func NaiveDiffDriverWithApply ( driver ApplyDiffProtoDriver , uidMaps , gidMaps [ ] idtools . IDMap ) graphdriver . Driver {
2014-08-19 11:23:55 +02:00
return & naiveDiffDriverWithApply {
2015-10-08 11:51:41 -04:00
Driver : graphdriver . NewNaiveDiffDriver ( driver , uidMaps , gidMaps ) ,
2014-08-19 11:23:55 +02:00
applyDiff : driver ,
}
}
2015-07-24 17:55:15 +00:00
// ApplyDiff creates a diff layer with either the NaiveDiffDriver or with a fallback.
2016-10-20 16:40:59 -07:00
func ( d * naiveDiffDriverWithApply ) ApplyDiff ( id , parent string , diff io . Reader ) ( int64 , error ) {
2014-08-19 11:23:55 +02:00
b , err := d . applyDiff . ApplyDiff ( id , parent , diff )
if err == ErrApplyDiffFallback {
return d . Driver . ApplyDiff ( id , parent , diff )
}
return b , err
}
2014-12-03 13:57:23 +01:00
// This backend uses the overlay union filesystem for containers
2014-08-19 11:23:55 +02:00
// plus hard link file sharing for images.
// Each container/image can have a "root" subdirectory which is a plain
2014-12-03 13:57:23 +01:00
// filesystem hierarchy, or they can use overlay.
2014-08-19 11:23:55 +02:00
2014-12-03 13:57:23 +01:00
// If they use overlay there is a "upper" directory and a "lower-id"
2014-08-19 11:23:55 +02:00
// file, as well as "merged" and "work" directories. The "upper"
// directory has the upper layer of the overlay, and "lower-id" contains
// the id of the parent whose "root" directory shall be used as the lower
// layer in the overlay. The overlay itself is mounted in the "merged"
2014-12-03 13:57:23 +01:00
// directory, and the "work" dir is needed for overlay to work.
2014-08-19 11:23:55 +02:00
2016-05-08 09:36:10 +08:00
// When an overlay layer is created there are two cases, either the
// parent has a "root" dir, then we start out with an empty "upper"
2014-08-19 11:23:55 +02:00
// directory overlaid on the parents root. This is typically the
// case with the init layer of a container which is based on an image.
// If there is no "root" in the parent, we inherit the lower-id from
2015-07-27 15:37:26 -07:00
// the parent and start by making a copy in the parent's "upper" dir.
2014-08-19 11:23:55 +02:00
// This is typically the case for a container layer which copies
// its parent -init upper layer.
// Additionally we also have a custom implementation of ApplyLayer
// which makes a recursive copy of the parent "root" layer using
// hardlinks to share file data, and then applies the layer on top
// of that. This means all child images share file (but not directory)
// data with the parent.
2018-05-11 13:40:46 +02:00
type overlayOptions struct { }
2015-07-24 17:55:15 +00:00
// Driver contains information about the home directory and the list of active mounts that are created using this driver.
2014-08-19 11:23:55 +02:00
type Driver struct {
2016-10-17 04:30:16 +00:00
home string
uidMaps [ ] idtools . IDMap
gidMaps [ ] idtools . IDMap
ctr * graphdriver . RefCounter
supportsDType bool
2017-02-17 15:46:19 -08:00
locker * locker . Locker
2014-08-19 11:23:55 +02:00
}
func init ( ) {
2014-12-03 13:57:23 +01:00
graphdriver . Register ( "overlay" , Init )
2014-08-19 11:23:55 +02:00
}
2015-07-24 17:55:15 +00:00
// Init returns the NaiveDiffDriver, a native diff driver for overlay filesystem.
2017-09-25 14:42:31 -07:00
// If overlay filesystem is not supported on the host, the error
// graphdriver.ErrNotSupported is returned.
// If an overlay filesystem is not supported over an existing filesystem then
// error graphdriver.ErrIncompatibleFS is returned.
2015-10-08 11:51:41 -04:00
func Init ( home string , options [ ] string , uidMaps , gidMaps [ ] idtools . IDMap ) ( graphdriver . Driver , error ) {
2018-05-11 13:40:46 +02:00
_ , err := parseOptions ( options )
if err != nil {
return nil , err
}
2015-01-15 16:40:39 -05:00
2014-12-03 14:06:19 +01:00
if err := supportsOverlay ( ) ; err != nil {
2014-08-19 11:23:55 +02:00
return nil , graphdriver . ErrNotSupported
}
2017-12-04 14:45:26 -08:00
// Perform feature detection on /var/lib/docker/overlay if it's an existing directory.
// This covers situations where /var/lib/docker/overlay is a mount, and on a different
// filesystem than /var/lib/docker.
// If the path does not exist, fall back to using /var/lib/docker for feature detection.
testdir := home
if _ , err := os . Stat ( testdir ) ; os . IsNotExist ( err ) {
testdir = filepath . Dir ( testdir )
}
fsMagic , err := graphdriver . GetFSMagic ( testdir )
2015-01-15 16:40:39 -05:00
if err != nil {
2014-12-28 15:08:42 -08:00
return nil , err
}
2015-01-15 16:40:39 -05:00
if fsName , ok := graphdriver . FsNames [ fsMagic ] ; ok {
backingFs = fsName
}
2014-12-28 15:08:42 -08:00
2015-01-15 16:40:39 -05:00
switch fsMagic {
2017-11-16 01:48:43 +01:00
case graphdriver . FsMagicAufs , graphdriver . FsMagicBtrfs , graphdriver . FsMagicEcryptfs , graphdriver . FsMagicNfsFs , graphdriver . FsMagicOverlay , graphdriver . FsMagicZfs :
2018-03-05 16:09:41 +01:00
logrus . WithField ( "storage-driver" , "overlay" ) . Errorf ( "'overlay' is not supported over %s" , backingFs )
2016-04-06 21:06:42 -07:00
return nil , graphdriver . ErrIncompatibleFS
2014-12-28 15:08:42 -08:00
}
2017-11-16 01:48:43 +01:00
supportsDType , err := fsutils . SupportsDType ( testdir )
if err != nil {
return nil , err
}
if ! supportsDType {
2017-12-04 17:02:52 -08:00
if ! graphdriver . IsInitialized ( home ) {
return nil , overlayutils . ErrDTypeNotSupported ( "overlay" , backingFs )
}
// allow running without d_type only for existing setups (#27443)
2018-03-05 16:09:41 +01:00
logrus . WithField ( "storage-driver" , "overlay" ) . Warn ( overlayutils . ErrDTypeNotSupported ( "overlay" , backingFs ) )
2017-11-16 01:48:43 +01:00
}
2015-10-08 11:51:41 -04:00
rootUID , rootGID , err := idtools . GetRootUIDGID ( uidMaps , gidMaps )
if err != nil {
return nil , err
}
2014-08-19 11:23:55 +02:00
// Create the driver home dir
2017-11-15 22:20:33 -08:00
if err := idtools . MkdirAllAndChown ( home , 0700 , idtools . Identity { UID : rootUID , GID : rootGID } ) ; err != nil {
2014-08-19 11:23:55 +02:00
return nil , err
}
d := & Driver {
2016-10-17 04:30:16 +00:00
home : home ,
uidMaps : uidMaps ,
gidMaps : gidMaps ,
ctr : graphdriver . NewRefCounter ( graphdriver . NewFsChecker ( graphdriver . FsMagicOverlay ) ) ,
supportsDType : supportsDType ,
2017-02-17 15:46:19 -08:00
locker : locker . New ( ) ,
2014-08-19 11:23:55 +02:00
}
2015-10-08 11:51:41 -04:00
return NaiveDiffDriverWithApply ( d , uidMaps , gidMaps ) , nil
2014-08-19 11:23:55 +02:00
}
2018-05-11 13:40:46 +02:00
func parseOptions ( options [ ] string ) ( * overlayOptions , error ) {
o := & overlayOptions { }
for _ , option := range options {
key , _ , err := parsers . ParseKeyValueOpt ( option )
if err != nil {
return nil , err
}
key = strings . ToLower ( key )
switch key {
default :
return nil , fmt . Errorf ( "overlay: unknown option %s" , key )
}
}
return o , nil
}
2014-12-03 14:06:19 +01:00
func supportsOverlay ( ) error {
2014-12-03 13:57:23 +01:00
// We can try to modprobe overlay first before looking at
// proc/filesystems for when overlay is supported
2014-11-30 10:14:34 +01:00
exec . Command ( "modprobe" , "overlay" ) . Run ( )
2014-08-19 11:23:55 +02:00
f , err := os . Open ( "/proc/filesystems" )
if err != nil {
return err
}
defer f . Close ( )
s := bufio . NewScanner ( f )
for s . Scan ( ) {
2014-11-30 10:14:34 +01:00
if s . Text ( ) == "nodev\toverlay" {
2014-08-19 11:23:55 +02:00
return nil
}
}
2018-03-05 16:09:41 +01:00
logrus . WithField ( "storage-driver" , "overlay" ) . Error ( "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded." )
2014-08-19 11:23:55 +02:00
return graphdriver . ErrNotSupported
}
func ( d * Driver ) String ( ) string {
2014-12-03 13:57:23 +01:00
return "overlay"
2014-08-19 11:23:55 +02:00
}
2015-07-24 17:55:15 +00:00
// Status returns current driver information in a two dimensional string array.
// Output contains "Backing Filesystem" used in this implementation.
2014-08-19 11:23:55 +02:00
func ( d * Driver ) Status ( ) [ ] [ 2 ] string {
2015-01-15 16:40:39 -05:00
return [ ] [ 2 ] string {
{ "Backing Filesystem" , backingFs } ,
2016-10-17 04:30:16 +00:00
{ "Supports d_type" , strconv . FormatBool ( d . supportsDType ) } ,
2015-01-15 16:40:39 -05:00
}
2014-08-19 11:23:55 +02:00
}
2017-09-25 14:42:31 -07:00
// GetMetadata returns metadata about the overlay driver such as root,
// LowerDir, UpperDir, WorkDir and MergeDir used to store data.
2015-06-15 14:05:10 -04:00
func ( d * Driver ) GetMetadata ( id string ) ( map [ string ] string , error ) {
2015-06-25 17:33:20 -04:00
dir := d . dir ( id )
if _ , err := os . Stat ( dir ) ; err != nil {
return nil , err
}
metadata := make ( map [ string ] string )
// If id has a root, it is an image
rootDir := path . Join ( dir , "root" )
if _ , err := os . Stat ( rootDir ) ; err == nil {
metadata [ "RootDir" ] = rootDir
return metadata , nil
}
2015-07-24 17:55:15 +00:00
lowerID , err := ioutil . ReadFile ( path . Join ( dir , "lower-id" ) )
2015-06-25 17:33:20 -04:00
if err != nil {
return nil , err
}
2015-07-24 17:55:15 +00:00
metadata [ "LowerDir" ] = path . Join ( d . dir ( string ( lowerID ) ) , "root" )
2015-06-25 17:33:20 -04:00
metadata [ "UpperDir" ] = path . Join ( dir , "upper" )
metadata [ "WorkDir" ] = path . Join ( dir , "work" )
metadata [ "MergedDir" ] = path . Join ( dir , "merged" )
return metadata , nil
2015-06-15 14:05:10 -04:00
}
2016-04-18 21:48:09 +00:00
// Cleanup any state created by overlay which should be cleaned when daemon
// is being shutdown. For now, we just have to unmount the bind mounted
// we had created.
2014-08-19 11:23:55 +02:00
func ( d * Driver ) Cleanup ( ) error {
2018-01-17 21:17:26 -05:00
return mount . RecursiveUnmount ( d . home )
2014-08-19 11:23:55 +02:00
}
2016-02-18 17:24:59 -08:00
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
2016-11-09 15:59:58 -05:00
func ( d * Driver ) CreateReadWrite ( id , parent string , opts * graphdriver . CreateOpts ) error {
return d . Create ( id , parent , opts )
2016-02-18 17:24:59 -08:00
}
2015-07-24 17:55:15 +00:00
// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
// The parent filesystem is used to configure these directories for the overlay.
2016-11-09 15:59:58 -05:00
func ( d * Driver ) Create ( id , parent string , opts * graphdriver . CreateOpts ) ( retErr error ) {
2016-03-20 00:42:58 -04:00
2016-11-09 15:59:58 -05:00
if opts != nil && len ( opts . StorageOpt ) != 0 {
2016-03-20 00:42:58 -04:00
return fmt . Errorf ( "--storage-opt is not supported for overlay" )
}
2014-08-19 11:23:55 +02:00
dir := d . dir ( id )
2015-10-08 11:51:41 -04:00
rootUID , rootGID , err := idtools . GetRootUIDGID ( d . uidMaps , d . gidMaps )
if err != nil {
return err
}
2017-11-15 22:20:33 -08:00
root := idtools . Identity { UID : rootUID , GID : rootGID }
2017-11-19 00:52:47 +01:00
if err := idtools . MkdirAllAndChown ( path . Dir ( dir ) , 0700 , root ) ; err != nil {
2014-08-19 11:23:55 +02:00
return err
}
2017-11-19 00:52:47 +01:00
if err := idtools . MkdirAndChown ( dir , 0700 , root ) ; err != nil {
2014-08-19 11:23:55 +02:00
return err
}
defer func ( ) {
// Clean up on failure
if retErr != nil {
os . RemoveAll ( dir )
}
} ( )
// Toplevel images are just a "root" dir
if parent == "" {
2017-11-19 00:52:47 +01:00
return idtools . MkdirAndChown ( path . Join ( dir , "root" ) , 0755 , root )
2014-08-19 11:23:55 +02:00
}
parentDir := d . dir ( parent )
// Ensure parent exists
if _ , err := os . Lstat ( parentDir ) ; err != nil {
return err
}
2016-05-08 09:36:10 +08:00
// If parent has a root, just do an overlay to it
2014-08-19 11:23:55 +02:00
parentRoot := path . Join ( parentDir , "root" )
if s , err := os . Lstat ( parentRoot ) ; err == nil {
2017-11-19 00:52:47 +01:00
if err := idtools . MkdirAndChown ( path . Join ( dir , "upper" ) , s . Mode ( ) , root ) ; err != nil {
2014-08-19 11:23:55 +02:00
return err
}
2017-11-19 00:52:47 +01:00
if err := idtools . MkdirAndChown ( path . Join ( dir , "work" ) , 0700 , root ) ; err != nil {
2014-08-19 11:23:55 +02:00
return err
}
2018-01-15 00:42:25 +01:00
return ioutil . WriteFile ( path . Join ( dir , "lower-id" ) , [ ] byte ( parent ) , 0666 )
2014-08-19 11:23:55 +02:00
}
// Otherwise, copy the upper and the lower-id from the parent
2015-07-24 17:55:15 +00:00
lowerID , err := ioutil . ReadFile ( path . Join ( parentDir , "lower-id" ) )
2014-08-19 11:23:55 +02:00
if err != nil {
return err
}
2015-07-24 17:55:15 +00:00
if err := ioutil . WriteFile ( path . Join ( dir , "lower-id" ) , lowerID , 0666 ) ; err != nil {
2014-08-19 11:23:55 +02:00
return err
}
parentUpperDir := path . Join ( parentDir , "upper" )
s , err := os . Lstat ( parentUpperDir )
if err != nil {
return err
}
upperDir := path . Join ( dir , "upper" )
2017-11-19 00:52:47 +01:00
if err := idtools . MkdirAndChown ( upperDir , s . Mode ( ) , root ) ; err != nil {
2014-08-19 11:23:55 +02:00
return err
}
2017-11-19 00:52:47 +01:00
if err := idtools . MkdirAndChown ( path . Join ( dir , "work" ) , 0700 , root ) ; err != nil {
2014-08-19 11:23:55 +02:00
return err
}
2017-11-21 10:29:27 -08:00
return copy . DirCopy ( parentUpperDir , upperDir , copy . Content , true )
2014-08-19 11:23:55 +02:00
}
func ( d * Driver ) dir ( id string ) string {
return path . Join ( d . home , id )
}
2015-07-24 17:55:15 +00:00
// Remove cleans the directories that are created for this id.
2014-08-19 11:23:55 +02:00
func ( d * Driver ) Remove ( id string ) error {
2018-01-30 16:02:59 +08:00
if id == "" {
return fmt . Errorf ( "refusing to remove the directories: id is empty" )
}
2017-02-17 15:46:19 -08:00
d . locker . Lock ( id )
defer d . locker . Unlock ( id )
2017-02-14 13:35:20 -05:00
return system . EnsureRemoveAll ( d . dir ( id ) )
2014-08-19 11:23:55 +02:00
}
2015-07-24 17:55:15 +00:00
// Get creates and mounts the required file system for the given id and returns the mount path.
2017-08-03 17:22:00 -07:00
func ( d * Driver ) Get ( id , mountLabel string ) ( _ containerfs . ContainerFS , err error ) {
2017-02-17 15:46:19 -08:00
d . locker . Lock ( id )
defer d . locker . Unlock ( id )
2014-08-19 11:23:55 +02:00
dir := d . dir ( id )
if _ , err := os . Stat ( dir ) ; err != nil {
2017-08-03 17:22:00 -07:00
return nil , err
2014-08-19 11:23:55 +02:00
}
2016-05-16 11:07:03 -07:00
// If id has a root, just return it
rootDir := path . Join ( dir , "root" )
if _ , err := os . Stat ( rootDir ) ; err == nil {
2017-08-03 17:22:00 -07:00
return containerfs . NewLocalContainerFS ( rootDir ) , nil
2016-05-16 11:07:03 -07:00
}
2017-11-20 21:33:11 -08:00
2016-05-02 15:44:20 -07:00
mergedDir := path . Join ( dir , "merged" )
if count := d . ctr . Increment ( mergedDir ) ; count > 1 {
2017-08-03 17:22:00 -07:00
return containerfs . NewLocalContainerFS ( mergedDir ) , nil
2016-05-02 15:44:20 -07:00
}
2016-05-02 16:36:20 -07:00
defer func ( ) {
if err != nil {
2016-05-16 11:07:03 -07:00
if c := d . ctr . Decrement ( mergedDir ) ; c <= 0 {
2017-11-20 21:33:11 -08:00
if mntErr := unix . Unmount ( mergedDir , 0 ) ; mntErr != nil {
2018-03-05 16:09:41 +01:00
logrus . WithField ( "storage-driver" , "overlay" ) . Debugf ( "Failed to unmount %s: %v: %v" , id , mntErr , err )
2017-11-20 21:33:11 -08:00
}
// Cleanup the created merged directory; see the comment in Put's rmdir
if rmErr := unix . Rmdir ( mergedDir ) ; rmErr != nil && ! os . IsNotExist ( rmErr ) {
2018-03-05 16:09:41 +01:00
logrus . WithField ( "storage-driver" , "overlay" ) . Warnf ( "Failed to remove %s: %v: %v" , id , rmErr , err )
2017-11-20 21:33:11 -08:00
}
2016-05-16 11:07:03 -07:00
}
2016-05-02 16:36:20 -07:00
}
} ( )
2015-07-24 17:55:15 +00:00
lowerID , err := ioutil . ReadFile ( path . Join ( dir , "lower-id" ) )
2014-08-19 11:23:55 +02:00
if err != nil {
2017-08-03 17:22:00 -07:00
return nil , err
2014-08-19 11:23:55 +02:00
}
2017-11-20 21:33:11 -08:00
rootUID , rootGID , err := idtools . GetRootUIDGID ( d . uidMaps , d . gidMaps )
if err != nil {
return nil , err
}
2017-11-15 22:20:33 -08:00
if err := idtools . MkdirAndChown ( mergedDir , 0700 , idtools . Identity { UID : rootUID , GID : rootGID } ) ; err != nil {
2017-11-20 21:33:11 -08:00
return nil , err
}
2016-05-02 16:36:20 -07:00
var (
lowerDir = path . Join ( d . dir ( string ( lowerID ) ) , "root" )
upperDir = path . Join ( dir , "upper" )
workDir = path . Join ( dir , "work" )
opts = fmt . Sprintf ( "lowerdir=%s,upperdir=%s,workdir=%s" , lowerDir , upperDir , workDir )
)
2017-05-23 10:22:32 -04:00
if err := unix . Mount ( "overlay" , mergedDir , "overlay" , 0 , label . FormatMountLabel ( opts , mountLabel ) ) ; err != nil {
2017-08-03 17:22:00 -07:00
return nil , fmt . Errorf ( "error creating overlay mount to %s: %v" , mergedDir , err )
2014-08-19 11:23:55 +02:00
}
2015-10-08 11:51:41 -04:00
// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
// user namespace requires this to move a directory from lower to upper.
if err := os . Chown ( path . Join ( workDir , "work" ) , rootUID , rootGID ) ; err != nil {
2017-08-03 17:22:00 -07:00
return nil , err
2015-10-08 11:51:41 -04:00
}
2017-08-03 17:22:00 -07:00
return containerfs . NewLocalContainerFS ( mergedDir ) , nil
2016-03-09 16:23:04 -05:00
}
2015-07-24 17:55:15 +00:00
// Put unmounts the mount path created for the give id.
2017-11-20 21:33:11 -08:00
// It also removes the 'merged' directory to force the kernel to unmount the
// overlay mount in other namespaces.
2015-01-09 17:14:52 -05:00
func ( d * Driver ) Put ( id string ) error {
2017-02-17 15:46:19 -08:00
d . locker . Lock ( id )
defer d . locker . Unlock ( id )
2016-10-25 14:40:13 -07:00
// If id has a root, just return
if _ , err := os . Stat ( path . Join ( d . dir ( id ) , "root" ) ) ; err == nil {
return nil
}
2016-05-02 16:36:20 -07:00
mountpoint := path . Join ( d . dir ( id ) , "merged" )
2018-03-05 16:09:41 +01:00
logger := logrus . WithField ( "storage-driver" , "overlay" )
2016-05-02 15:44:20 -07:00
if count := d . ctr . Decrement ( mountpoint ) ; count > 0 {
return nil
}
2017-05-23 10:22:32 -04:00
if err := unix . Unmount ( mountpoint , unix . MNT_DETACH ) ; err != nil {
2018-03-05 16:09:41 +01:00
logger . Debugf ( "Failed to unmount %s overlay: %v" , id , err )
2014-08-19 11:23:55 +02:00
}
2017-11-20 21:33:11 -08:00
// Remove the mountpoint here. Removing the mountpoint (in newer kernels)
// will cause all other instances of this mount in other mount namespaces
// to be unmounted. This is necessary to avoid cases where an overlay mount
// that is present in another namespace will cause subsequent mounts
// operations to fail with ebusy. We ignore any errors here because this may
// fail on older kernels which don't have
// torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied.
if err := unix . Rmdir ( mountpoint ) ; err != nil {
2018-03-05 16:09:41 +01:00
logger . Debugf ( "Failed to remove %s overlay: %v" , id , err )
2017-11-20 21:33:11 -08:00
}
2015-01-09 17:14:52 -05:00
return nil
2014-08-19 11:23:55 +02:00
}
2016-05-08 09:36:10 +08:00
// ApplyDiff applies the new layer on top of the root, if parent does not exist with will return an ErrApplyDiffFallback error.
2016-10-20 16:40:59 -07:00
func ( d * Driver ) ApplyDiff ( id string , parent string , diff io . Reader ) ( size int64 , err error ) {
2014-08-19 11:23:55 +02:00
dir := d . dir ( id )
if parent == "" {
return 0 , ErrApplyDiffFallback
}
parentRootDir := path . Join ( d . dir ( parent ) , "root" )
if _ , err := os . Stat ( parentRootDir ) ; err != nil {
return 0 , ErrApplyDiffFallback
}
// We now know there is a parent, and it has a "root" directory containing
// the full root filesystem. We can just hardlink it and apply the
// layer. This relies on two things:
// 1) ApplyDiff is only run once on a clean (no writes to upper layer) container
// 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks)
// These are all currently true and are not expected to break
tmpRootDir , err := ioutil . TempDir ( dir , "tmproot" )
if err != nil {
return 0 , err
}
defer func ( ) {
if err != nil {
os . RemoveAll ( tmpRootDir )
} else {
os . RemoveAll ( path . Join ( dir , "upper" ) )
os . RemoveAll ( path . Join ( dir , "work" ) )
os . RemoveAll ( path . Join ( dir , "merged" ) )
os . RemoveAll ( path . Join ( dir , "lower-id" ) )
}
} ( )
2017-11-21 10:29:27 -08:00
if err = copy . DirCopy ( parentRootDir , tmpRootDir , copy . Hardlink , true ) ; err != nil {
2014-08-19 11:23:55 +02:00
return 0 , err
}
2015-10-08 11:51:41 -04:00
options := & archive . TarOptions { UIDMaps : d . uidMaps , GIDMaps : d . gidMaps }
2016-04-15 15:53:37 -07:00
if size , err = graphdriver . ApplyUncompressedLayer ( tmpRootDir , diff , options ) ; err != nil {
2014-08-19 11:23:55 +02:00
return 0 , err
}
rootDir := path . Join ( dir , "root" )
if err := os . Rename ( tmpRootDir , rootDir ) ; err != nil {
return 0 , err
}
2014-12-17 18:26:03 -08:00
return
2014-08-19 11:23:55 +02:00
}
2015-07-24 17:55:15 +00:00
// Exists checks to see if the id is already mounted.
2014-08-19 11:23:55 +02:00
func ( d * Driver ) Exists ( id string ) bool {
_ , err := os . Stat ( d . dir ( id ) )
return err == nil
}