2016-05-17 00:02:51 +00:00
// +build linux
2018-02-05 21:05:59 +00:00
package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2"
2016-05-17 00:02:51 +00:00
import (
"bufio"
2018-03-29 15:34:58 +00:00
"context"
2016-05-17 00:02:51 +00:00
"errors"
"fmt"
2016-10-20 23:40:59 +00:00
"io"
2016-05-17 00:02:51 +00:00
"io/ioutil"
"os"
"os/exec"
"path"
2016-04-21 16:08:37 +00:00
"path/filepath"
2016-07-11 23:48:08 +00:00
"strconv"
2016-05-17 00:02:51 +00:00
"strings"
2016-11-09 21:07:56 +00:00
"sync"
2016-05-17 00:02:51 +00:00
"github.com/docker/docker/daemon/graphdriver"
2016-10-17 04:30:16 +00:00
"github.com/docker/docker/daemon/graphdriver/overlayutils"
2016-10-18 00:27:24 +00:00
"github.com/docker/docker/daemon/graphdriver/quota"
2016-05-17 00:02:51 +00:00
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/chrootarchive"
2017-08-04 00:22:00 +00:00
"github.com/docker/docker/pkg/containerfs"
2016-05-17 00:02:51 +00:00
"github.com/docker/docker/pkg/directory"
2016-10-17 04:30:16 +00:00
"github.com/docker/docker/pkg/fsutils"
2016-05-17 00:02:51 +00:00
"github.com/docker/docker/pkg/idtools"
2017-02-17 23:46:19 +00:00
"github.com/docker/docker/pkg/locker"
2016-05-17 00:02:51 +00:00
"github.com/docker/docker/pkg/mount"
2016-07-11 23:48:08 +00:00
"github.com/docker/docker/pkg/parsers"
2016-05-17 00:02:51 +00:00
"github.com/docker/docker/pkg/parsers/kernel"
2017-02-14 18:35:20 +00:00
"github.com/docker/docker/pkg/system"
2017-11-16 16:34:33 +00:00
"github.com/docker/go-units"
2017-12-14 04:38:22 +00:00
rsystem "github.com/opencontainers/runc/libcontainer/system"
2017-04-18 13:26:36 +00:00
"github.com/opencontainers/selinux/go-selinux/label"
2017-11-16 16:34:33 +00:00
"github.com/sirupsen/logrus"
2017-05-23 14:22:32 +00:00
"golang.org/x/sys/unix"
2016-05-17 00:02:51 +00:00
)
var (
// untar defines the untar method
untar = chrootarchive . UntarUncompressed
)
// This backend uses the overlay union filesystem for containers
// with diff directories for each layer.
// This version of the overlay driver requires at least kernel
// 4.0.0 in order to support mounting multiple diff directories.
// Each container/image has at least a "diff" directory and "link" file.
// If there is also a "lower" file when there are diff layers
2016-04-21 16:08:37 +00:00
// below as well as "merged" and "work" directories. The "diff" directory
2016-05-17 00:02:51 +00:00
// has the upper layer of the overlay and is used to capture any
// changes to the layer. The "lower" file contains all the lower layer
// mounts separated by ":" and ordered from uppermost to lowermost
// layers. The overlay itself is mounted in the "merged" directory,
// and the "work" dir is needed for overlay to work.
// The "link" file for each layer contains a unique string for the layer.
// Under the "l" directory at the root there will be a symbolic link
// with that unique string pointing the "diff" directory for the layer.
// The symbolic links are used to reference lower layers in the "lower"
// file and on mount. The links are used to shorten the total length
// of a layer reference without requiring changes to the layer identifier
// or root directory. Mounts are always done relative to root and
// referencing the symbolic links in order to ensure the number of
// lower directories can fit in a single page for making the mount
// syscall. A hard upper limit of 128 lower layers is enforced to ensure
// that mounts do not fail due to length.
const (
driverName = "overlay2"
linkDir = "l"
lowerFile = "lower"
maxDepth = 128
// idLength represents the number of random characters
2017-09-25 21:42:31 +00:00
// which can be used to create the unique link identifier
2016-05-17 00:02:51 +00:00
// for every layer. If this value is too long then the
// page size limit for the mount command may be exceeded.
// The idLength should be selected such that following equation
// is true (512 is a buffer for label metadata).
// ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512)
idLength = 26
)
2016-09-18 19:35:55 +00:00
type overlayOptions struct {
overrideKernelCheck bool
2016-10-18 00:27:24 +00:00
quota quota . Quota
2016-09-18 19:35:55 +00:00
}
2017-09-25 21:42:31 +00:00
// Driver contains information about the home directory and the list of active
// mounts that are created using this driver.
2016-05-17 00:02:51 +00:00
type Driver struct {
2016-10-17 04:30:16 +00:00
home string
uidMaps [ ] idtools . IDMap
gidMaps [ ] idtools . IDMap
ctr * graphdriver . RefCounter
quotaCtl * quota . Control
options overlayOptions
naiveDiff graphdriver . DiffDriver
supportsDType bool
2017-02-17 23:46:19 +00:00
locker * locker . Locker
2016-05-17 00:02:51 +00:00
}
2016-09-18 19:35:55 +00:00
var (
backingFs = "<unknown>"
projectQuotaSupported = false
2016-11-09 21:07:56 +00:00
useNaiveDiffLock sync . Once
useNaiveDiffOnly bool
2016-09-18 19:35:55 +00:00
)
2016-05-17 00:02:51 +00:00
func init ( ) {
graphdriver . Register ( driverName , Init )
}
2017-09-25 21:42:31 +00:00
// Init returns the native diff driver for overlay filesystem.
// 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
// the error graphdriver.ErrIncompatibleFS is returned.
2016-05-17 00:02:51 +00:00
func Init ( home string , options [ ] string , uidMaps , gidMaps [ ] idtools . IDMap ) ( graphdriver . Driver , error ) {
2016-07-11 23:48:08 +00:00
opts , err := parseOptions ( options )
if err != nil {
return nil , err
}
2016-05-17 00:02:51 +00:00
if err := supportsOverlay ( ) ; err != nil {
return nil , graphdriver . ErrNotSupported
}
// require kernel 4.0.0 to ensure multiple lower dirs are supported
v , err := kernel . GetKernelVersion ( )
if err != nil {
return nil , err
}
2017-12-04 22:45:26 +00:00
// Perform feature detection on /var/lib/docker/overlay2 if it's an existing directory.
// This covers situations where /var/lib/docker/overlay2 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 )
2016-05-17 00:02:51 +00:00
if err != nil {
return nil , err
}
if fsName , ok := graphdriver . FsNames [ fsMagic ] ; ok {
backingFs = fsName
}
2018-03-05 15:09:41 +00:00
logger := logrus . WithField ( "storage-driver" , "overlay2" )
2016-05-17 00:02:51 +00:00
switch fsMagic {
2017-11-16 00:48:43 +00:00
case graphdriver . FsMagicAufs , graphdriver . FsMagicEcryptfs , graphdriver . FsMagicNfsFs , graphdriver . FsMagicOverlay , graphdriver . FsMagicZfs :
2018-03-05 15:09:41 +00:00
logger . Errorf ( "'overlay2' is not supported over %s" , backingFs )
2016-05-17 00:02:51 +00:00
return nil , graphdriver . ErrIncompatibleFS
2017-05-18 22:26:03 +00:00
case graphdriver . FsMagicBtrfs :
// Support for OverlayFS on BTRFS was added in kernel 4.7
// See https://btrfs.wiki.kernel.org/index.php/Changelog
if kernel . CompareKernelVersion ( * v , kernel . VersionInfo { Kernel : 4 , Major : 7 , Minor : 0 } ) < 0 {
if ! opts . overrideKernelCheck {
2018-03-05 15:09:41 +00:00
logger . Errorf ( "'overlay2' requires kernel 4.7 to use on %s" , backingFs )
2017-05-18 22:26:03 +00:00
return nil , graphdriver . ErrIncompatibleFS
}
2018-03-05 15:09:41 +00:00
logger . Warn ( "Using pre-4.7.0 kernel for overlay2 on btrfs, may require kernel update" )
2017-05-18 22:26:03 +00:00
}
2016-05-17 00:02:51 +00:00
}
2017-11-16 16:34:33 +00:00
if kernel . CompareKernelVersion ( * v , kernel . VersionInfo { Kernel : 4 , Major : 0 , Minor : 0 } ) < 0 {
if opts . overrideKernelCheck {
2018-03-05 15:09:41 +00:00
logger . Warn ( "Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update" )
2017-11-16 16:34:33 +00:00
} else {
2017-11-16 00:48:43 +00:00
if err := supportsMultipleLowerDir ( testdir ) ; err != nil {
2018-03-05 15:09:41 +00:00
logger . Debugf ( "Multiple lower dirs not supported: %v" , err )
2017-11-16 16:34:33 +00:00
return nil , graphdriver . ErrNotSupported
}
}
}
2017-11-16 00:48:43 +00:00
supportsDType , err := fsutils . SupportsDType ( testdir )
if err != nil {
return nil , err
}
if ! supportsDType {
2017-12-05 01:02:52 +00:00
if ! graphdriver . IsInitialized ( home ) {
return nil , overlayutils . ErrDTypeNotSupported ( "overlay2" , backingFs )
}
// allow running without d_type only for existing setups (#27443)
2018-03-05 15:09:41 +00:00
logger . Warn ( overlayutils . ErrDTypeNotSupported ( "overlay2" , backingFs ) )
2017-11-16 00:48:43 +00:00
}
2017-11-16 16:34:33 +00:00
2016-05-17 00:02:51 +00:00
rootUID , rootGID , err := idtools . GetRootUIDGID ( uidMaps , gidMaps )
if err != nil {
return nil , err
}
// Create the driver home dir
2017-11-16 06:20:33 +00:00
if err := idtools . MkdirAllAndChown ( path . Join ( home , linkDir ) , 0700 , idtools . Identity { UID : rootUID , GID : rootGID } ) ; err != nil {
2016-05-17 00:02:51 +00: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 23:46:19 +00:00
locker : locker . New ( ) ,
2017-05-10 11:24:35 +00:00
options : * opts ,
2016-05-17 00:02:51 +00:00
}
2016-04-21 16:08:37 +00:00
d . naiveDiff = graphdriver . NewNaiveDiffDriver ( d , uidMaps , gidMaps )
2016-09-18 19:35:55 +00:00
if backingFs == "xfs" {
// Try to enable project quota support over xfs.
2016-10-18 00:28:14 +00:00
if d . quotaCtl , err = quota . NewControl ( home ) ; err == nil {
2016-09-18 19:35:55 +00:00
projectQuotaSupported = true
2017-05-10 11:24:35 +00:00
} else if opts . quota . Size > 0 {
return nil , fmt . Errorf ( "Storage option overlay2.size not supported. Filesystem does not support Project Quota: %v" , err )
2016-09-18 19:35:55 +00:00
}
2017-05-10 11:24:35 +00:00
} else if opts . quota . Size > 0 {
// if xfs is not the backing fs then error out if the storage-opt overlay2.size is used.
return nil , fmt . Errorf ( "Storage Option overlay2.size only supported for backingFS XFS. Found %v" , backingFs )
2016-09-18 19:35:55 +00:00
}
2016-05-17 00:02:51 +00:00
2018-03-05 15:09:41 +00:00
logger . Debugf ( "backingFs=%s, projectQuotaSupported=%v" , backingFs , projectQuotaSupported )
2016-09-18 19:35:55 +00:00
return d , nil
2016-07-11 23:48:08 +00:00
}
func parseOptions ( options [ ] string ) ( * overlayOptions , error ) {
o := & overlayOptions { }
for _ , option := range options {
key , val , err := parsers . ParseKeyValueOpt ( option )
if err != nil {
return nil , err
}
key = strings . ToLower ( key )
switch key {
case "overlay2.override_kernel_check" :
o . overrideKernelCheck , err = strconv . ParseBool ( val )
if err != nil {
return nil , err
}
2017-05-10 11:24:35 +00:00
case "overlay2.size" :
size , err := units . RAMInBytes ( val )
if err != nil {
return nil , err
}
o . quota . Size = uint64 ( size )
2016-07-11 23:48:08 +00:00
default :
2017-05-10 11:24:35 +00:00
return nil , fmt . Errorf ( "overlay2: unknown option %s" , key )
2016-07-11 23:48:08 +00:00
}
}
return o , nil
}
2016-05-17 00:02:51 +00:00
func supportsOverlay ( ) error {
// We can try to modprobe overlay first before looking at
// proc/filesystems for when overlay is supported
exec . Command ( "modprobe" , "overlay" ) . Run ( )
f , err := os . Open ( "/proc/filesystems" )
if err != nil {
return err
}
defer f . Close ( )
s := bufio . NewScanner ( f )
for s . Scan ( ) {
if s . Text ( ) == "nodev\toverlay" {
return nil
}
}
2018-03-05 15:09:41 +00:00
logrus . WithField ( "storage-driver" , "overlay2" ) . Error ( "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded." )
2016-05-17 00:02:51 +00:00
return graphdriver . ErrNotSupported
}
2016-11-09 21:07:56 +00:00
func useNaiveDiff ( home string ) bool {
useNaiveDiffLock . Do ( func ( ) {
2017-08-01 12:05:27 +00:00
if err := doesSupportNativeDiff ( home ) ; err != nil {
2018-03-05 15:09:41 +00:00
logrus . WithField ( "storage-driver" , "overlay2" ) . Warnf ( "Not using native diff for overlay2, this may cause degraded performance for building images: %v" , err )
2016-11-09 21:07:56 +00:00
useNaiveDiffOnly = true
}
} )
return useNaiveDiffOnly
}
2016-05-17 00:02:51 +00:00
func ( d * Driver ) String ( ) string {
return driverName
}
// Status returns current driver information in a two dimensional string array.
// Output contains "Backing Filesystem" used in this implementation.
func ( d * Driver ) Status ( ) [ ] [ 2 ] string {
return [ ] [ 2 ] string {
{ "Backing Filesystem" , backingFs } ,
2016-10-17 04:30:16 +00:00
{ "Supports d_type" , strconv . FormatBool ( d . supportsDType ) } ,
2016-11-09 21:07:56 +00:00
{ "Native Overlay Diff" , strconv . FormatBool ( ! useNaiveDiff ( d . home ) ) } ,
2016-05-17 00:02:51 +00:00
}
}
2017-09-25 21:42:31 +00:00
// GetMetadata returns metadata about the overlay driver such as the LowerDir,
// UpperDir, WorkDir, and MergeDir used to store data.
2016-05-17 00:02:51 +00:00
func ( d * Driver ) GetMetadata ( id string ) ( map [ string ] string , error ) {
dir := d . dir ( id )
if _ , err := os . Stat ( dir ) ; err != nil {
return nil , err
}
metadata := map [ string ] string {
"WorkDir" : path . Join ( dir , "work" ) ,
"MergedDir" : path . Join ( dir , "merged" ) ,
"UpperDir" : path . Join ( dir , "diff" ) ,
}
lowerDirs , err := d . getLowerDirs ( id )
if err != nil {
return nil , err
}
if len ( lowerDirs ) > 0 {
metadata [ "LowerDir" ] = strings . Join ( lowerDirs , ":" )
}
return metadata , nil
}
// 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.
func ( d * Driver ) Cleanup ( ) error {
2018-01-18 02:17:26 +00:00
return mount . RecursiveUnmount ( d . home )
2016-05-17 00:02:51 +00:00
}
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
2016-11-09 20:59:58 +00:00
func ( d * Driver ) CreateReadWrite ( id , parent string , opts * graphdriver . CreateOpts ) error {
2017-05-10 11:24:35 +00:00
if opts != nil && len ( opts . StorageOpt ) != 0 && ! projectQuotaSupported {
return fmt . Errorf ( "--storage-opt is supported only for overlay over xfs with 'pquota' mount option" )
}
if opts == nil {
opts = & graphdriver . CreateOpts {
StorageOpt : map [ string ] string { } ,
}
}
if _ , ok := opts . StorageOpt [ "size" ] ; ! ok {
if opts . StorageOpt == nil {
opts . StorageOpt = map [ string ] string { }
}
opts . StorageOpt [ "size" ] = strconv . FormatUint ( d . options . quota . Size , 10 )
}
return d . create ( id , parent , opts )
2016-05-17 00:02:51 +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 20:59:58 +00:00
func ( d * Driver ) Create ( id , parent string , opts * graphdriver . CreateOpts ) ( retErr error ) {
2017-05-10 11:24:35 +00:00
if opts != nil && len ( opts . StorageOpt ) != 0 {
if _ , ok := opts . StorageOpt [ "size" ] ; ok {
return fmt . Errorf ( "--storage-opt size is only supported for ReadWrite Layers" )
}
2016-05-17 00:02:51 +00:00
}
2017-05-10 11:24:35 +00:00
return d . create ( id , parent , opts )
}
2016-05-17 00:02:51 +00:00
2017-05-10 11:24:35 +00:00
func ( d * Driver ) create ( id , parent string , opts * graphdriver . CreateOpts ) ( retErr error ) {
2016-05-17 00:02:51 +00:00
dir := d . dir ( id )
rootUID , rootGID , err := idtools . GetRootUIDGID ( d . uidMaps , d . gidMaps )
if err != nil {
return err
}
2017-11-16 06:20:33 +00:00
root := idtools . Identity { UID : rootUID , GID : rootGID }
2017-11-18 23:52:47 +00:00
if err := idtools . MkdirAllAndChown ( path . Dir ( dir ) , 0700 , root ) ; err != nil {
2016-05-17 00:02:51 +00:00
return err
}
2017-11-18 23:52:47 +00:00
if err := idtools . MkdirAndChown ( dir , 0700 , root ) ; err != nil {
2016-05-17 00:02:51 +00:00
return err
}
defer func ( ) {
// Clean up on failure
if retErr != nil {
os . RemoveAll ( dir )
}
} ( )
2016-11-09 20:59:58 +00:00
if opts != nil && len ( opts . StorageOpt ) > 0 {
2016-09-18 19:35:55 +00:00
driver := & Driver { }
2016-11-09 20:59:58 +00:00
if err := d . parseStorageOpt ( opts . StorageOpt , driver ) ; err != nil {
2016-09-18 19:35:55 +00:00
return err
}
if driver . options . quota . Size > 0 {
// Set container disk quota limit
if err := d . quotaCtl . SetQuota ( dir , driver . options . quota ) ; err != nil {
return err
}
}
}
2017-11-18 23:52:47 +00:00
if err := idtools . MkdirAndChown ( path . Join ( dir , "diff" ) , 0755 , root ) ; err != nil {
2016-05-17 00:02:51 +00:00
return err
}
lid := generateID ( idLength )
if err := os . Symlink ( path . Join ( ".." , id , "diff" ) , path . Join ( d . home , linkDir , lid ) ) ; err != nil {
return err
}
// Write link id to link file
if err := ioutil . WriteFile ( path . Join ( dir , "link" ) , [ ] byte ( lid ) , 0644 ) ; err != nil {
return err
}
// if no parent directory, done
if parent == "" {
return nil
}
2017-11-18 23:52:47 +00:00
if err := idtools . MkdirAndChown ( path . Join ( dir , "work" ) , 0700 , root ) ; err != nil {
2016-05-17 00:02:51 +00:00
return err
}
lower , err := d . getLower ( parent )
if err != nil {
return err
}
if lower != "" {
if err := ioutil . WriteFile ( path . Join ( dir , lowerFile ) , [ ] byte ( lower ) , 0666 ) ; err != nil {
return err
}
}
return nil
}
2016-09-18 19:35:55 +00:00
// Parse overlay storage options
func ( d * Driver ) parseStorageOpt ( storageOpt map [ string ] string , driver * Driver ) error {
// Read size to set the disk project quota per container
for key , val := range storageOpt {
key := strings . ToLower ( key )
switch key {
case "size" :
size , err := units . RAMInBytes ( val )
if err != nil {
return err
}
driver . options . quota . Size = uint64 ( size )
default :
return fmt . Errorf ( "Unknown option %s" , key )
}
}
return nil
}
2016-05-17 00:02:51 +00:00
func ( d * Driver ) getLower ( parent string ) ( string , error ) {
parentDir := d . dir ( parent )
// Ensure parent exists
if _ , err := os . Lstat ( parentDir ) ; err != nil {
return "" , err
}
// Read Parent link fileA
parentLink , err := ioutil . ReadFile ( path . Join ( parentDir , "link" ) )
if err != nil {
return "" , err
}
lowers := [ ] string { path . Join ( linkDir , string ( parentLink ) ) }
parentLower , err := ioutil . ReadFile ( path . Join ( parentDir , lowerFile ) )
if err == nil {
parentLowers := strings . Split ( string ( parentLower ) , ":" )
lowers = append ( lowers , parentLowers ... )
}
if len ( lowers ) > maxDepth {
return "" , errors . New ( "max depth exceeded" )
}
return strings . Join ( lowers , ":" ) , nil
}
func ( d * Driver ) dir ( id string ) string {
return path . Join ( d . home , id )
}
func ( d * Driver ) getLowerDirs ( id string ) ( [ ] string , error ) {
var lowersArray [ ] string
lowers , err := ioutil . ReadFile ( path . Join ( d . dir ( id ) , lowerFile ) )
if err == nil {
for _ , s := range strings . Split ( string ( lowers ) , ":" ) {
lp , err := os . Readlink ( path . Join ( d . home , s ) )
if err != nil {
return nil , err
}
2016-11-02 23:13:53 +00:00
lowersArray = append ( lowersArray , path . Clean ( path . Join ( d . home , linkDir , lp ) ) )
2016-05-17 00:02:51 +00:00
}
} else if ! os . IsNotExist ( err ) {
return nil , err
}
return lowersArray , nil
}
// Remove cleans the directories that are created for this id.
func ( d * Driver ) Remove ( id string ) error {
2018-01-30 08:02:59 +00:00
if id == "" {
return fmt . Errorf ( "refusing to remove the directories: id is empty" )
}
2017-02-17 23:46:19 +00:00
d . locker . Lock ( id )
defer d . locker . Unlock ( id )
2016-06-14 19:40:35 +00:00
dir := d . dir ( id )
lid , err := ioutil . ReadFile ( path . Join ( dir , "link" ) )
if err == nil {
2018-01-30 08:02:59 +00:00
if len ( lid ) == 0 {
logrus . WithField ( "storage-driver" , "overlay2" ) . Errorf ( "refusing to remove empty link for layer %v" , id )
} else if err := os . RemoveAll ( path . Join ( d . home , linkDir , string ( lid ) ) ) ; err != nil {
2018-03-05 15:09:41 +00:00
logrus . WithField ( "storage-driver" , "overlay2" ) . Debugf ( "Failed to remove link: %v" , err )
2016-06-14 19:40:35 +00:00
}
}
2017-02-14 18:35:20 +00:00
if err := system . EnsureRemoveAll ( dir ) ; err != nil && ! os . IsNotExist ( err ) {
2016-05-17 00:02:51 +00:00
return err
}
return nil
}
// Get creates and mounts the required file system for the given id and returns the mount path.
2017-09-20 22:20:43 +00:00
func ( d * Driver ) Get ( id , mountLabel string ) ( _ containerfs . ContainerFS , retErr error ) {
2017-02-17 23:46:19 +00:00
d . locker . Lock ( id )
defer d . locker . Unlock ( id )
2016-05-17 00:02:51 +00:00
dir := d . dir ( id )
if _ , err := os . Stat ( dir ) ; err != nil {
2017-08-04 00:22:00 +00:00
return nil , err
2016-05-17 00:02:51 +00:00
}
diffDir := path . Join ( dir , "diff" )
lowers , err := ioutil . ReadFile ( path . Join ( dir , lowerFile ) )
if err != nil {
// If no lower, just return diff directory
if os . IsNotExist ( err ) {
2017-08-04 00:22:00 +00:00
return containerfs . NewLocalContainerFS ( diffDir ) , nil
2016-05-17 00:02:51 +00:00
}
2017-08-04 00:22:00 +00:00
return nil , err
2016-05-17 00:02:51 +00:00
}
mergedDir := path . Join ( dir , "merged" )
if count := d . ctr . Increment ( mergedDir ) ; count > 1 {
2017-08-04 00:22:00 +00:00
return containerfs . NewLocalContainerFS ( mergedDir ) , nil
2016-05-17 00:02:51 +00:00
}
defer func ( ) {
2017-09-20 22:20:43 +00:00
if retErr != nil {
2016-05-17 00:02:51 +00:00
if c := d . ctr . Decrement ( mergedDir ) ; c <= 0 {
2017-09-20 22:20:43 +00:00
if mntErr := unix . Unmount ( mergedDir , 0 ) ; mntErr != nil {
2018-03-05 15:09:41 +00:00
logrus . WithField ( "storage-driver" , "overlay2" ) . Errorf ( "error unmounting %v: %v" , mergedDir , mntErr )
2017-09-20 22:20:43 +00:00
}
2017-11-21 05:33:11 +00: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 15:09:41 +00:00
logrus . WithField ( "storage-driver" , "overlay2" ) . Debugf ( "Failed to remove %s: %v: %v" , id , rmErr , err )
2017-11-21 05:33:11 +00:00
}
2016-05-17 00:02:51 +00:00
}
}
} ( )
workDir := path . Join ( dir , "work" )
2016-08-22 18:43:10 +00:00
splitLowers := strings . Split ( string ( lowers ) , ":" )
absLowers := make ( [ ] string , len ( splitLowers ) )
for i , s := range splitLowers {
absLowers [ i ] = path . Join ( d . home , s )
}
opts := fmt . Sprintf ( "lowerdir=%s,upperdir=%s,workdir=%s" , strings . Join ( absLowers , ":" ) , path . Join ( dir , "diff" ) , path . Join ( dir , "work" ) )
mountData := label . FormatMountLabel ( opts , mountLabel )
2017-05-23 14:22:32 +00:00
mount := unix . Mount
2016-08-22 18:43:10 +00:00
mountTarget := mergedDir
2017-11-21 05:33:11 +00:00
rootUID , rootGID , err := idtools . GetRootUIDGID ( d . uidMaps , d . gidMaps )
if err != nil {
return nil , err
}
2017-11-16 06:20:33 +00:00
if err := idtools . MkdirAndChown ( mergedDir , 0700 , idtools . Identity { UID : rootUID , GID : rootGID } ) ; err != nil {
2017-11-21 05:33:11 +00:00
return nil , err
}
2017-05-23 14:22:32 +00:00
pageSize := unix . Getpagesize ( )
2016-08-22 18:43:10 +00:00
2016-10-19 00:34:19 +00:00
// Go can return a larger page size than supported by the system
// as of go 1.7. This will be fixed in 1.8 and this block can be
// removed when building with 1.8.
// See https://github.com/golang/go/commit/1b9499b06989d2831e5b156161d6c07642926ee1
// See https://github.com/docker/docker/issues/27384
if pageSize > 4096 {
pageSize = 4096
}
2016-08-22 18:43:10 +00:00
// Use relative paths and mountFrom when the mount data has exceeded
// the page size. The mount syscall fails if the mount data cannot
// fit within a page and relative links make the mount data much
// smaller at the expense of requiring a fork exec to chroot.
if len ( mountData ) > pageSize {
opts = fmt . Sprintf ( "lowerdir=%s,upperdir=%s,workdir=%s" , string ( lowers ) , path . Join ( id , "diff" ) , path . Join ( id , "work" ) )
mountData = label . FormatMountLabel ( opts , mountLabel )
if len ( mountData ) > pageSize {
2017-08-04 00:22:00 +00:00
return nil , fmt . Errorf ( "cannot mount layer, mount label too large %d" , len ( mountData ) )
2016-08-22 18:43:10 +00:00
}
mount = func ( source string , target string , mType string , flags uintptr , label string ) error {
return mountFrom ( d . home , source , target , mType , flags , label )
}
mountTarget = path . Join ( id , "merged" )
2016-05-17 00:02:51 +00:00
}
2016-08-22 18:43:10 +00:00
if err := mount ( "overlay" , mountTarget , "overlay" , 0 , mountData ) ; err != nil {
2017-08-04 00:22:00 +00:00
return nil , fmt . Errorf ( "error creating overlay mount to %s: %v" , mergedDir , err )
2016-05-17 00:02:51 +00: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-04 00:22:00 +00:00
return nil , err
2016-05-17 00:02:51 +00:00
}
2017-08-04 00:22:00 +00:00
return containerfs . NewLocalContainerFS ( mergedDir ) , nil
2016-05-17 00:02:51 +00:00
}
// Put unmounts the mount path created for the give id.
2017-11-21 05:33:11 +00:00
// It also removes the 'merged' directory to force the kernel to unmount the
// overlay mount in other namespaces.
2016-05-17 00:02:51 +00:00
func ( d * Driver ) Put ( id string ) error {
2017-02-17 23:46:19 +00:00
d . locker . Lock ( id )
defer d . locker . Unlock ( id )
2017-02-16 01:55:50 +00:00
dir := d . dir ( id )
_ , err := ioutil . ReadFile ( path . Join ( dir , lowerFile ) )
if err != nil {
// If no lower, no mount happened and just return directly
if os . IsNotExist ( err ) {
return nil
}
return err
}
mountpoint := path . Join ( dir , "merged" )
2018-03-05 15:09:41 +00:00
logger := logrus . WithField ( "storage-driver" , "overlay2" )
2016-05-17 00:02:51 +00:00
if count := d . ctr . Decrement ( mountpoint ) ; count > 0 {
return nil
}
2017-05-23 14:22:32 +00:00
if err := unix . Unmount ( mountpoint , unix . MNT_DETACH ) ; err != nil {
2018-03-05 15:09:41 +00:00
logger . Debugf ( "Failed to unmount %s overlay: %s - %v" , id , mountpoint , err )
2016-05-17 00:02:51 +00:00
}
2017-11-21 05:33:11 +00: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 && ! os . IsNotExist ( err ) {
2018-03-05 15:09:41 +00:00
logger . Debugf ( "Failed to remove %s overlay: %v" , id , err )
2017-11-21 05:33:11 +00:00
}
2016-05-17 00:02:51 +00:00
return nil
}
// Exists checks to see if the id is already mounted.
func ( d * Driver ) Exists ( id string ) bool {
_ , err := os . Stat ( d . dir ( id ) )
return err == nil
}
2017-09-25 21:42:31 +00:00
// isParent determines whether the given parent is the direct parent of the
// given layer id
2016-04-21 16:08:37 +00:00
func ( d * Driver ) isParent ( id , parent string ) bool {
lowers , err := d . getLowerDirs ( id )
if err != nil {
return false
}
if parent == "" && len ( lowers ) > 0 {
return false
}
parentDir := d . dir ( parent )
var ld string
if len ( lowers ) > 0 {
ld = filepath . Dir ( lowers [ 0 ] )
}
if ld == "" && parent == "" {
return true
}
return ld == parentDir
}
2016-05-17 00:02:51 +00:00
// ApplyDiff applies the new layer into a root
2016-10-20 23:40:59 +00:00
func ( d * Driver ) ApplyDiff ( id string , parent string , diff io . Reader ) ( size int64 , err error ) {
2016-04-21 16:08:37 +00:00
if ! d . isParent ( id , parent ) {
return d . naiveDiff . ApplyDiff ( id , parent , diff )
}
2016-05-17 00:02:51 +00:00
applyDir := d . getDiffPath ( id )
2018-03-05 15:09:41 +00:00
logrus . WithField ( "storage-driver" , "overlay2" ) . Debugf ( "Applying tar in %s" , applyDir )
2016-05-17 00:02:51 +00:00
// Overlay doesn't need the parent id to apply the diff
if err := untar ( diff , applyDir , & archive . TarOptions {
UIDMaps : d . uidMaps ,
GIDMaps : d . gidMaps ,
WhiteoutFormat : archive . OverlayWhiteoutFormat ,
2017-12-14 04:38:22 +00:00
InUserNS : rsystem . RunningInUserNS ( ) ,
2016-05-17 00:02:51 +00:00
} ) ; err != nil {
return 0 , err
}
2018-03-29 15:34:58 +00:00
return directory . Size ( context . TODO ( ) , applyDir )
2016-05-17 00:02:51 +00:00
}
func ( d * Driver ) getDiffPath ( id string ) string {
dir := d . dir ( id )
return path . Join ( dir , "diff" )
}
// DiffSize calculates the changes between the specified id
// and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory.
func ( d * Driver ) DiffSize ( id , parent string ) ( size int64 , err error ) {
2016-11-09 21:07:56 +00:00
if useNaiveDiff ( d . home ) || ! d . isParent ( id , parent ) {
2016-04-21 16:08:37 +00:00
return d . naiveDiff . DiffSize ( id , parent )
}
2018-03-29 15:34:58 +00:00
return directory . Size ( context . TODO ( ) , d . getDiffPath ( id ) )
2016-05-17 00:02:51 +00:00
}
// Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "".
2016-10-20 23:40:59 +00:00
func ( d * Driver ) Diff ( id , parent string ) ( io . ReadCloser , error ) {
2016-11-09 21:07:56 +00:00
if useNaiveDiff ( d . home ) || ! d . isParent ( id , parent ) {
2016-04-21 16:08:37 +00:00
return d . naiveDiff . Diff ( id , parent )
}
2016-05-17 00:02:51 +00:00
diffPath := d . getDiffPath ( id )
2018-03-05 15:09:41 +00:00
logrus . WithField ( "storage-driver" , "overlay2" ) . Debugf ( "Tar with options on %s" , diffPath )
2016-05-17 00:02:51 +00:00
return archive . TarWithOptions ( diffPath , & archive . TarOptions {
Compression : archive . Uncompressed ,
UIDMaps : d . uidMaps ,
GIDMaps : d . gidMaps ,
WhiteoutFormat : archive . OverlayWhiteoutFormat ,
} )
}
2017-09-25 21:42:31 +00:00
// Changes produces a list of changes between the specified layer and its
// parent layer. If parent is "", then all changes will be ADD changes.
2016-05-17 00:02:51 +00:00
func ( d * Driver ) Changes ( id , parent string ) ( [ ] archive . Change , error ) {
2018-06-19 22:56:41 +00:00
return d . naiveDiff . Changes ( id , parent )
2016-05-17 00:02:51 +00:00
}