2014-08-02 03:46:15 -04:00
// +build linux
2013-11-27 22:12:51 -05:00
2013-09-04 05:18:23 -04:00
package devmapper
import (
2015-11-11 12:07:35 -05:00
"bufio"
2013-09-04 05:18:23 -04:00
"encoding/json"
2013-11-29 22:02:09 -05:00
"errors"
2013-09-04 05:18:23 -04:00
"fmt"
"io"
"io/ioutil"
2014-05-16 08:10:02 -04:00
"os"
"os/exec"
2013-09-04 05:18:23 -04:00
"path"
"path/filepath"
2013-10-03 15:00:16 -04:00
"strconv"
2014-03-27 12:48:32 -04:00
"strings"
2013-10-07 08:06:24 -04:00
"sync"
2014-03-20 12:32:59 -04:00
"syscall"
2013-10-17 18:04:14 -04:00
"time"
2014-04-28 17:36:04 -04:00
2015-03-26 18:22:04 -04:00
"github.com/Sirupsen/logrus"
2015-10-08 11:51:41 -04:00
2014-10-24 18:11:48 -04:00
"github.com/docker/docker/daemon/graphdriver"
2016-04-01 16:31:44 -04:00
"github.com/docker/docker/dockerversion"
2014-11-05 18:10:38 -05:00
"github.com/docker/docker/pkg/devicemapper"
2015-10-08 11:51:41 -04:00
"github.com/docker/docker/pkg/idtools"
2015-12-14 17:16:34 -05:00
"github.com/docker/docker/pkg/loopback"
2015-09-08 15:23:44 -04:00
"github.com/docker/docker/pkg/mount"
2014-07-28 20:23:38 -04:00
"github.com/docker/docker/pkg/parsers"
2015-12-16 12:26:49 -05:00
"github.com/docker/go-units"
2015-10-08 11:51:41 -04:00
2015-07-16 19:00:55 -04:00
"github.com/opencontainers/runc/libcontainer/label"
2013-09-04 05:18:23 -04:00
)
2013-10-11 23:37:11 -04:00
var (
2015-07-22 20:42:28 -04:00
defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024
defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024
2016-01-12 13:44:13 -05:00
defaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024
2015-07-22 20:42:28 -04:00
defaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors
defaultUdevSyncOverride = false
maxDeviceID = 0xffffff // 24 bit, pool limit
deviceIDMapSz = ( maxDeviceID + 1 ) / 8
2015-04-02 16:47:14 -04:00
// We retry device removal so many a times that even error messages
// will fill up console during normal operation. So only log Fatal
// messages by default.
2016-03-07 15:27:39 -05:00
logLevel = devicemapper . LogLevelFatal
driverDeferredRemovalSupport = false
enableDeferredRemoval = false
enableDeferredDeletion = false
userBaseSize = false
defaultMinFreeSpacePercent uint32 = 10
2013-10-02 23:18:15 -04:00
)
2013-09-04 05:18:23 -04:00
2014-11-07 16:53:34 -05:00
const deviceSetMetaFile string = "deviceset-metadata"
2014-12-03 13:06:43 -05:00
const transactionMetaFile string = "transaction-metadata"
2014-11-07 16:53:34 -05:00
2015-07-22 20:42:28 -04:00
type transaction struct {
OpenTransactionID uint64 ` json:"open_transaction_id" `
DeviceIDHash string ` json:"device_hash" `
DeviceID int ` json:"device_id" `
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
type devInfo struct {
2014-12-12 14:01:46 -05:00
Hash string ` json:"-" `
2015-07-22 20:42:28 -04:00
DeviceID int ` json:"device_id" `
2014-12-12 14:01:46 -05:00
Size uint64 ` json:"size" `
2015-07-22 20:42:28 -04:00
TransactionID uint64 ` json:"transaction_id" `
2014-12-12 14:01:46 -05:00
Initialized bool ` json:"initialized" `
2015-10-06 17:37:21 -04:00
Deleted bool ` json:"deleted" `
2014-12-12 14:01:46 -05:00
devices * DeviceSet
2014-02-04 12:19:09 -05:00
2014-03-06 12:14:56 -05:00
// The global DeviceSet lock guarantees that we serialize all
// the calls to libdevmapper (which is not threadsafe), but we
// sometimes release that lock while sleeping. In that case
// this per-device lock is still held, protecting against
// other accesses to the device that we're doing the wait on.
2014-04-01 05:28:21 -04:00
//
// WARNING: In order to avoid AB-BA deadlocks when releasing
// the global lock while holding the per-device locks all
2015-08-07 18:24:18 -04:00
// device locks must be acquired *before* the device lock, and
// multiple device locks should be acquired parent before child.
2014-12-12 14:01:46 -05:00
lock sync . Mutex
2013-09-04 05:18:23 -04:00
}
2015-07-22 20:42:28 -04:00
type metaData struct {
2015-10-01 13:02:55 -04:00
Devices map [ string ] * devInfo ` json:"Devices" `
2013-09-04 05:18:23 -04:00
}
2015-07-22 20:42:28 -04:00
// DeviceSet holds information about list of devices
2013-10-17 20:07:34 -04:00
type DeviceSet struct {
2015-07-22 20:42:28 -04:00
metaData ` json:"-" `
2015-10-01 13:02:55 -04:00
sync . Mutex ` json:"-" ` // Protects all fields of DeviceSet and serializes calls into libdevmapper
2014-12-03 13:06:43 -05:00
root string
devicePrefix string
2015-07-22 20:42:28 -04:00
TransactionID uint64 ` json:"-" `
NextDeviceID int ` json:"next_device_id" `
deviceIDMap [ ] byte
2014-03-27 12:48:32 -04:00
// Options
2015-07-07 15:27:19 -04:00
dataLoopbackSize int64
metaDataLoopbackSize int64
baseFsSize uint64
2015-11-13 13:56:21 -05:00
filesystem string
2015-07-07 15:27:19 -04:00
mountOptions string
mkfsArgs [ ] string
dataDevice string // block or loop dev
dataLoopFile string // loopback file, if used
metadataDevice string // block or loop dev
metadataLoopFile string // loopback file, if used
doBlkDiscard bool
thinpBlockSize uint32
thinPoolDevice string
2015-07-22 20:42:28 -04:00
transaction ` json:"-" `
2015-07-07 15:27:19 -04:00
overrideUdevSyncCheck bool
deferredRemove bool // use deferred removal
2015-10-06 17:37:21 -04:00
deferredDelete bool // use deferred deletion
2015-12-11 11:39:29 -05:00
BaseDeviceUUID string // save UUID of base device
BaseDeviceFilesystem string // save filesystem of base device
nrDeletedDevices uint // number of deleted devices
2015-10-06 17:37:21 -04:00
deletionWorkerTicker * time . Ticker
2015-10-08 11:51:41 -04:00
uidMaps [ ] idtools . IDMap
gidMaps [ ] idtools . IDMap
2016-03-07 15:27:39 -05:00
minFreeSpacePercent uint32 //min free space percentage in thinpool
2013-09-04 05:18:23 -04:00
}
2015-07-22 20:42:28 -04:00
// DiskUsage contains information about disk usage and is used when reporting Status of a device.
2013-10-17 05:16:50 -04:00
type DiskUsage struct {
2015-07-22 20:42:28 -04:00
// Used bytes on the disk.
Used uint64
// Total bytes on the disk.
Total uint64
// Available bytes on the disk.
2015-01-31 01:21:34 -05:00
Available uint64
2013-10-17 05:16:50 -04:00
}
2015-07-22 20:42:28 -04:00
// Status returns the information about the device.
2013-10-17 05:16:50 -04:00
type Status struct {
2015-07-22 20:42:28 -04:00
// PoolName is the name of the data pool.
PoolName string
// DataFile is the actual block device for data.
DataFile string
// DataLoopback loopback file, if used.
DataLoopback string
// MetadataFile is the actual block device for metadata.
MetadataFile string
// MetadataLoopback is the loopback file, if used.
MetadataLoopback string
// Data is the disk used for data.
Data DiskUsage
// Metadata is the disk used for meta data.
Metadata DiskUsage
2015-10-10 10:52:05 -04:00
// BaseDeviceSize is base size of container and image
BaseDeviceSize uint64
2015-11-13 13:56:21 -05:00
// BaseDeviceFS is backing filesystem.
BaseDeviceFS string
2015-07-22 20:42:28 -04:00
// SectorSize size of the vector.
SectorSize uint64
// UdevSyncSupported is true if sync is supported.
UdevSyncSupported bool
// DeferredRemoveEnabled is true then the device is not unmounted.
2015-04-21 18:14:59 -04:00
DeferredRemoveEnabled bool
2015-10-06 17:37:21 -04:00
// True if deferred deletion is enabled. This is different from
// deferred removal. "removal" means that device mapper device is
// deactivated. Thin device is still in thin pool and can be activated
// again. But "deletion" means that thin device will be deleted from
// thin pool and it can't be activated again.
2015-10-06 17:37:21 -04:00
DeferredDeleteEnabled bool
DeferredDeletedDeviceCount uint
2016-04-21 11:42:23 -04:00
MinFreeSpace uint64
2013-11-18 05:33:07 -05:00
}
2015-06-15 14:05:10 -04:00
// Structure used to export image/container metadata in docker inspect.
2015-07-22 20:42:28 -04:00
type deviceMetadata struct {
deviceID int
2015-06-15 14:05:10 -04:00
deviceSize uint64 // size in bytes
deviceName string // Device name as used during activation
}
2015-07-22 20:42:28 -04:00
// DevStatus returns information about device mounted containing its id, size and sector information.
2013-11-18 05:33:07 -05:00
type DevStatus struct {
2015-07-22 20:42:28 -04:00
// DeviceID is the id of the device.
DeviceID int
// Size is the size of the filesystem.
Size uint64
// TransactionID is a unique integer per device set used to identify an operation on the file system, this number is incremental.
TransactionID uint64
// SizeInSectors indicates the size of the sectors allocated.
SizeInSectors uint64
// MappedSectors indicates number of mapped sectors.
MappedSectors uint64
// HighestMappedSector is the pointer to the highest mapped sector.
2013-11-18 05:33:07 -05:00
HighestMappedSector uint64
2013-10-17 05:16:50 -04:00
}
2013-09-04 05:18:23 -04:00
func getDevName ( name string ) string {
return "/dev/mapper/" + name
}
2015-07-22 20:42:28 -04:00
func ( info * devInfo ) Name ( ) string {
2013-09-04 05:18:23 -04:00
hash := info . Hash
if hash == "" {
hash = "base"
}
2013-09-05 09:32:57 -04:00
return fmt . Sprintf ( "%s-%s" , info . devices . devicePrefix , hash )
2013-09-04 05:18:23 -04:00
}
2015-07-22 20:42:28 -04:00
func ( info * devInfo ) DevName ( ) string {
2013-09-04 05:18:23 -04:00
return getDevName ( info . Name ( ) )
}
2013-10-17 20:07:34 -04:00
func ( devices * DeviceSet ) loopbackDir ( ) string {
2013-10-15 14:30:06 -04:00
return path . Join ( devices . root , "devicemapper" )
2013-09-04 05:18:23 -04:00
}
2014-04-24 17:49:44 -04:00
func ( devices * DeviceSet ) metadataDir ( ) string {
return path . Join ( devices . root , "metadata" )
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) metadataFile ( info * devInfo ) string {
2014-04-24 17:49:44 -04:00
file := info . Hash
if file == "" {
file = "base"
}
return path . Join ( devices . metadataDir ( ) , file )
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) transactionMetaFile ( ) string {
return path . Join ( devices . metadataDir ( ) , transactionMetaFile )
}
2014-11-05 09:25:02 -05:00
func ( devices * DeviceSet ) deviceSetMetaFile ( ) string {
2014-11-07 16:53:34 -05:00
return path . Join ( devices . metadataDir ( ) , deviceSetMetaFile )
2014-11-05 09:25:02 -05:00
}
2014-04-24 17:49:44 -04:00
func ( devices * DeviceSet ) oldMetadataFile ( ) string {
2013-09-04 05:18:23 -04:00
return path . Join ( devices . loopbackDir ( ) , "json" )
}
2013-10-17 20:07:34 -04:00
func ( devices * DeviceSet ) getPoolName ( ) string {
2014-10-24 19:25:24 -04:00
if devices . thinPoolDevice == "" {
return devices . devicePrefix + "-pool"
}
2014-11-21 21:36:23 -05:00
return devices . thinPoolDevice
2013-09-04 05:18:23 -04:00
}
2013-10-17 20:07:34 -04:00
func ( devices * DeviceSet ) getPoolDevName ( ) string {
2013-09-04 05:18:23 -04:00
return getDevName ( devices . getPoolName ( ) )
}
2013-10-17 20:07:34 -04:00
func ( devices * DeviceSet ) hasImage ( name string ) bool {
2013-09-04 05:18:23 -04:00
dirname := devices . loopbackDir ( )
filename := path . Join ( dirname , name )
2014-05-16 08:10:02 -04:00
_ , err := os . Stat ( filename )
2013-09-04 05:18:23 -04:00
return err == nil
}
2013-10-16 21:42:05 -04:00
// ensureImage creates a sparse file of <size> bytes at the path
// <root>/devicemapper/<name>.
2015-09-23 21:21:28 -04:00
// If the file already exists and new size is larger than its current size, it grows to the new size.
2013-10-16 21:42:05 -04:00
// Either way it returns the full path.
2013-10-17 20:07:34 -04:00
func ( devices * DeviceSet ) ensureImage ( name string , size int64 ) ( string , error ) {
2013-09-04 05:18:23 -04:00
dirname := devices . loopbackDir ( )
filename := path . Join ( dirname , name )
2015-10-08 11:51:41 -04:00
uid , gid , err := idtools . GetRootUIDGID ( devices . uidMaps , devices . gidMaps )
if err != nil {
return "" , err
}
if err := idtools . MkdirAllAs ( dirname , 0700 , uid , gid ) ; err != nil && ! os . IsExist ( err ) {
2013-09-04 05:18:23 -04:00
return "" , err
}
2015-09-23 21:21:28 -04:00
if fi , err := os . Stat ( filename ) ; err != nil {
2014-05-16 08:10:02 -04:00
if ! os . IsNotExist ( err ) {
2013-09-04 05:18:23 -04:00
return "" , err
}
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Creating loopback file %s for device-manage use" , filename )
2014-05-16 08:10:02 -04:00
file , err := os . OpenFile ( filename , os . O_RDWR | os . O_CREATE , 0600 )
2013-09-04 05:18:23 -04:00
if err != nil {
return "" , err
}
2013-11-13 20:05:32 -05:00
defer file . Close ( )
2013-10-02 23:18:15 -04:00
2015-04-26 12:50:25 -04:00
if err := file . Truncate ( size ) ; err != nil {
2013-09-04 05:18:23 -04:00
return "" , err
}
2015-09-23 21:21:28 -04:00
} else {
if fi . Size ( ) < size {
file , err := os . OpenFile ( filename , os . O_RDWR | os . O_CREATE , 0600 )
if err != nil {
return "" , err
}
defer file . Close ( )
if err := file . Truncate ( size ) ; err != nil {
2015-12-14 16:03:33 -05:00
return "" , fmt . Errorf ( "devmapper: Unable to grow loopback file %s: %v" , filename , err )
2015-09-23 21:21:28 -04:00
}
} else if fi . Size ( ) > size {
2015-12-14 16:03:33 -05:00
logrus . Warnf ( "devmapper: Can't shrink loopback file %s" , filename )
2015-09-23 21:21:28 -04:00
}
2013-09-04 05:18:23 -04:00
}
return filename , nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) allocateTransactionID ( ) uint64 {
devices . OpenTransactionID = devices . TransactionID + 1
return devices . OpenTransactionID
2013-09-04 05:18:23 -04:00
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) updatePoolTransactionID ( ) error {
2015-09-04 17:02:29 -04:00
if err := devicemapper . SetTransactionID ( devices . getPoolDevName ( ) , devices . TransactionID , devices . OpenTransactionID ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error setting devmapper transaction ID: %s" , err )
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
devices . TransactionID = devices . OpenTransactionID
2014-12-03 13:06:43 -05:00
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) removeMetadata ( info * devInfo ) error {
2014-05-16 08:10:02 -04:00
if err := os . RemoveAll ( devices . metadataFile ( info ) ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error removing metadata file %s: %s" , devices . metadataFile ( info ) , err )
2014-04-24 17:49:44 -04:00
}
return nil
}
2014-11-05 09:25:02 -05:00
// Given json data and file path, write it to disk
func ( devices * DeviceSet ) writeMetaFile ( jsonData [ ] byte , filePath string ) error {
2014-04-24 17:49:44 -04:00
tmpFile , err := ioutil . TempFile ( devices . metadataDir ( ) , ".tmp" )
2013-09-04 05:18:23 -04:00
if err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error creating metadata file: %s" , err )
2013-09-04 05:18:23 -04:00
}
n , err := tmpFile . Write ( jsonData )
if err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error writing metadata to %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 05:18:23 -04:00
}
if n < len ( jsonData ) {
2013-10-02 23:18:15 -04:00
return io . ErrShortWrite
2013-09-04 05:18:23 -04:00
}
2013-10-02 23:18:15 -04:00
if err := tmpFile . Sync ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error syncing metadata file %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 05:18:23 -04:00
}
2013-10-02 23:18:15 -04:00
if err := tmpFile . Close ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error closing metadata file %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 05:18:23 -04:00
}
2014-11-05 09:25:02 -05:00
if err := os . Rename ( tmpFile . Name ( ) , filePath ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error committing metadata file %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 05:18:23 -04:00
}
2014-11-05 09:25:02 -05:00
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) saveMetadata ( info * devInfo ) error {
2014-11-05 09:25:02 -05:00
jsonData , err := json . Marshal ( info )
if err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error encoding metadata to json: %s" , err )
2014-11-05 09:25:02 -05:00
}
2014-11-07 16:53:34 -05:00
if err := devices . writeMetaFile ( jsonData , devices . metadataFile ( info ) ) ; err != nil {
2014-11-05 09:25:02 -05:00
return err
}
2013-09-04 05:18:23 -04:00
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) markDeviceIDUsed ( deviceID int ) {
2014-12-03 13:06:43 -05:00
var mask byte
2015-07-22 20:42:28 -04:00
i := deviceID % 8
2014-12-03 13:06:43 -05:00
mask = 1 << uint ( i )
2015-07-22 20:42:28 -04:00
devices . deviceIDMap [ deviceID / 8 ] = devices . deviceIDMap [ deviceID / 8 ] | mask
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) markDeviceIDFree ( deviceID int ) {
2014-12-03 13:06:43 -05:00
var mask byte
2015-07-22 20:42:28 -04:00
i := deviceID % 8
2014-12-03 13:06:43 -05:00
mask = ^ ( 1 << uint ( i ) )
2015-07-22 20:42:28 -04:00
devices . deviceIDMap [ deviceID / 8 ] = devices . deviceIDMap [ deviceID / 8 ] & mask
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) isDeviceIDFree ( deviceID int ) bool {
2014-12-03 13:06:43 -05:00
var mask byte
2015-07-22 20:42:28 -04:00
i := deviceID % 8
2014-12-03 13:06:43 -05:00
mask = ( 1 << uint ( i ) )
2015-07-22 20:42:28 -04:00
if ( devices . deviceIDMap [ deviceID / 8 ] & mask ) != 0 {
2014-12-03 13:06:43 -05:00
return false
}
return true
}
2015-10-01 13:02:55 -04:00
// Should be called with devices.Lock() held.
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) lookupDevice ( hash string ) ( * devInfo , error ) {
2014-04-01 04:45:40 -04:00
info := devices . Devices [ hash ]
if info == nil {
2014-04-24 17:49:44 -04:00
info = devices . loadMetadata ( hash )
if info == nil {
2015-12-14 16:03:33 -05:00
return nil , fmt . Errorf ( "devmapper: Unknown device %s" , hash )
2014-04-24 17:49:44 -04:00
}
devices . Devices [ hash ] = info
2014-04-01 04:45:40 -04:00
}
return info , nil
}
2015-10-01 13:02:55 -04:00
func ( devices * DeviceSet ) lookupDeviceWithLock ( hash string ) ( * devInfo , error ) {
devices . Lock ( )
defer devices . Unlock ( )
info , err := devices . lookupDevice ( hash )
return info , err
}
2015-10-06 17:37:21 -04:00
// This function relies on that device hash map has been loaded in advance.
// Should be called with devices.Lock() held.
func ( devices * DeviceSet ) constructDeviceIDMap ( ) {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: constructDeviceIDMap()" )
defer logrus . Debugf ( "devmapper: constructDeviceIDMap() END" )
2015-10-06 17:37:21 -04:00
for _ , info := range devices . Devices {
devices . markDeviceIDUsed ( info . DeviceID )
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Added deviceId=%d to DeviceIdMap" , info . DeviceID )
2015-10-06 17:37:21 -04:00
}
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) deviceFileWalkFunction ( path string , finfo os . FileInfo ) error {
// Skip some of the meta files which are not device files.
if strings . HasSuffix ( finfo . Name ( ) , ".migrated" ) {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Skipping file %s" , path )
2014-12-03 13:06:43 -05:00
return nil
}
2015-01-29 13:19:18 -05:00
if strings . HasPrefix ( finfo . Name ( ) , "." ) {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Skipping file %s" , path )
2015-01-29 13:19:18 -05:00
return nil
}
2014-12-03 13:06:43 -05:00
if finfo . Name ( ) == deviceSetMetaFile {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Skipping file %s" , path )
2014-12-03 13:06:43 -05:00
return nil
}
2015-09-30 15:21:22 -04:00
if finfo . Name ( ) == transactionMetaFile {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Skipping file %s" , path )
2015-09-30 15:21:22 -04:00
return nil
}
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Loading data for file %s" , path )
2014-12-03 13:06:43 -05:00
hash := finfo . Name ( )
if hash == "base" {
hash = ""
}
2015-10-06 17:37:21 -04:00
// Include deleted devices also as cleanup delete device logic
// will go through it and see if there are any deleted devices.
2015-10-06 17:37:21 -04:00
if _ , err := devices . lookupDevice ( hash ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error looking up device %s:%v" , hash , err )
2014-12-03 13:06:43 -05:00
}
return nil
}
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) loadDeviceFilesOnStart ( ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: loadDeviceFilesOnStart()" )
defer logrus . Debugf ( "devmapper: loadDeviceFilesOnStart() END" )
2014-12-03 13:06:43 -05:00
var scan = func ( path string , info os . FileInfo , err error ) error {
if err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Can't walk the file %s" , path )
2014-12-03 13:06:43 -05:00
return nil
}
// Skip any directories
if info . IsDir ( ) {
return nil
}
return devices . deviceFileWalkFunction ( path , info )
}
return filepath . Walk ( devices . metadataDir ( ) , scan )
}
2015-10-01 13:02:55 -04:00
// Should be called with devices.Lock() held.
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) unregisterDevice ( id int , hash string ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: unregisterDevice(%v, %v)" , id , hash )
2015-07-22 20:42:28 -04:00
info := & devInfo {
2014-12-03 13:06:43 -05:00
Hash : hash ,
2015-07-22 20:42:28 -04:00
DeviceID : id ,
2014-12-03 13:06:43 -05:00
}
delete ( devices . Devices , hash )
if err := devices . removeMetadata ( info ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error removing metadata: %s" , err )
2014-12-03 13:06:43 -05:00
return err
}
return nil
}
2015-10-01 13:02:55 -04:00
// Should be called with devices.Lock() held.
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) registerDevice ( id int , hash string , size uint64 , transactionID uint64 ) ( * devInfo , error ) {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: registerDevice(%v, %v)" , id , hash )
2015-07-22 20:42:28 -04:00
info := & devInfo {
2013-09-04 05:18:23 -04:00
Hash : hash ,
2015-07-22 20:42:28 -04:00
DeviceID : id ,
2013-09-04 05:18:23 -04:00
Size : size ,
2015-07-22 20:42:28 -04:00
TransactionID : transactionID ,
2013-09-04 05:18:23 -04:00
Initialized : false ,
2013-09-05 09:32:57 -04:00
devices : devices ,
2013-09-04 05:18:23 -04:00
}
devices . Devices [ hash ] = info
2014-04-01 05:05:30 -04:00
2014-04-24 17:49:44 -04:00
if err := devices . saveMetadata ( info ) ; err != nil {
2013-09-04 05:18:23 -04:00
// Try to remove unused device
2013-10-02 23:18:15 -04:00
delete ( devices . Devices , hash )
2013-09-04 05:18:23 -04:00
return nil , err
}
return info , nil
}
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) activateDeviceIfNeeded ( info * devInfo , ignoreDeleted bool ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: activateDeviceIfNeeded(%v)" , info . Hash )
2013-09-04 05:18:23 -04:00
2015-10-06 17:37:21 -04:00
if info . Deleted && ! ignoreDeleted {
return fmt . Errorf ( "devmapper: Can't activate device %v as it is marked for deletion" , info . Hash )
}
2015-04-21 18:14:59 -04:00
// Make sure deferred removal on device is canceled, if one was
// scheduled.
if err := devices . cancelDeferredRemoval ( info ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Device Deferred Removal Cancellation Failed: %s" , err )
2015-04-21 18:14:59 -04:00
}
2014-11-05 18:10:38 -05:00
if devinfo , _ := devicemapper . GetInfo ( info . Name ( ) ) ; devinfo != nil && devinfo . Exists != 0 {
2013-09-04 05:18:23 -04:00
return nil
}
2015-07-22 20:42:28 -04:00
return devicemapper . ActivateDevice ( devices . getPoolDevName ( ) , info . Name ( ) , info . DeviceID , info . Size )
2013-09-04 05:18:23 -04:00
}
2015-11-11 12:07:35 -05:00
// Return true only if kernel supports xfs and mkfs.xfs is available
func xfsSupported ( ) bool {
// Make sure mkfs.xfs is available
if _ , err := exec . LookPath ( "mkfs.xfs" ) ; err != nil {
return false
}
// Check if kernel supports xfs filesystem or not.
exec . Command ( "modprobe" , "xfs" ) . Run ( )
f , err := os . Open ( "/proc/filesystems" )
if err != nil {
2015-12-14 16:03:33 -05:00
logrus . Warnf ( "devmapper: Could not check if xfs is supported: %v" , err )
2015-11-11 12:07:35 -05:00
return false
}
defer f . Close ( )
s := bufio . NewScanner ( f )
for s . Scan ( ) {
if strings . HasSuffix ( s . Text ( ) , "\txfs" ) {
return true
}
}
if err := s . Err ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Warnf ( "devmapper: Could not check if xfs is supported: %v" , err )
2015-11-11 12:07:35 -05:00
}
return false
}
2015-11-11 12:07:35 -05:00
func determineDefaultFS ( ) string {
2015-11-11 12:07:35 -05:00
if xfsSupported ( ) {
return "xfs"
}
2016-02-22 14:22:20 -05:00
logrus . Warn ( "devmapper: XFS is not supported in your system. Either the kernel doesn't support it or mkfs.xfs is not in your PATH. Defaulting to ext4 filesystem" )
2015-11-11 12:07:35 -05:00
return "ext4"
}
2015-12-01 08:05:46 -05:00
func ( devices * DeviceSet ) createFilesystem ( info * devInfo ) ( err error ) {
2013-09-04 05:18:23 -04:00
devname := info . DevName ( )
2014-03-18 09:23:43 -04:00
args := [ ] string { }
for _ , arg := range devices . mkfsArgs {
args = append ( args , arg )
}
args = append ( args , devname )
2015-11-13 13:56:21 -05:00
if devices . filesystem == "" {
devices . filesystem = determineDefaultFS ( )
2015-11-11 12:07:35 -05:00
}
2015-12-11 11:39:29 -05:00
if err := devices . saveBaseDeviceFilesystem ( devices . filesystem ) ; err != nil {
return err
}
2015-11-11 12:07:35 -05:00
2015-12-01 08:05:46 -05:00
logrus . Infof ( "devmapper: Creating filesystem %s on device %s" , devices . filesystem , info . Name ( ) )
defer func ( ) {
if err != nil {
logrus . Infof ( "devmapper: Error while creating filesystem %s on device %s: %v" , devices . filesystem , info . Name ( ) , err )
} else {
logrus . Infof ( "devmapper: Successfully created filesystem %s on device %s" , devices . filesystem , info . Name ( ) )
}
} ( )
2015-11-13 13:56:21 -05:00
switch devices . filesystem {
2014-03-18 09:23:43 -04:00
case "xfs" :
err = exec . Command ( "mkfs.xfs" , args ... ) . Run ( )
case "ext4" :
err = exec . Command ( "mkfs.ext4" , append ( [ ] string { "-E" , "nodiscard,lazy_itable_init=0,lazy_journal_init=0" } , args ... ) ... ) . Run ( )
if err != nil {
err = exec . Command ( "mkfs.ext4" , append ( [ ] string { "-E" , "nodiscard,lazy_itable_init=0" } , args ... ) ... ) . Run ( )
}
2014-09-05 22:32:39 -04:00
if err != nil {
return err
}
err = exec . Command ( "tune2fs" , append ( [ ] string { "-c" , "-1" , "-i" , "0" } , devname ) ... ) . Run ( )
2014-03-18 09:23:43 -04:00
default :
2015-12-14 16:03:33 -05:00
err = fmt . Errorf ( "devmapper: Unsupported filesystem type %s" , devices . filesystem )
2013-09-04 05:18:23 -04:00
}
2015-12-01 08:05:46 -05:00
return
2013-09-04 05:18:23 -04:00
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) migrateOldMetaData ( ) error {
2015-03-01 13:11:15 -05:00
// Migrate old metadata file
2014-04-24 17:49:44 -04:00
jsonData , err := ioutil . ReadFile ( devices . oldMetadataFile ( ) )
2014-05-16 08:10:02 -04:00
if err != nil && ! os . IsNotExist ( err ) {
2013-09-04 05:18:23 -04:00
return err
}
if jsonData != nil {
2015-07-22 20:42:28 -04:00
m := metaData { Devices : make ( map [ string ] * devInfo ) }
2014-04-24 17:49:44 -04:00
if err := json . Unmarshal ( jsonData , & m ) ; err != nil {
2013-09-04 05:18:23 -04:00
return err
}
2014-04-24 17:49:44 -04:00
for hash , info := range m . Devices {
info . Hash = hash
2014-12-03 13:06:43 -05:00
devices . saveMetadata ( info )
2013-09-04 05:18:23 -04:00
}
2014-05-16 08:10:02 -04:00
if err := os . Rename ( devices . oldMetadataFile ( ) , devices . oldMetadataFile ( ) + ".migrated" ) ; err != nil {
2014-04-24 17:49:44 -04:00
return err
2013-09-04 05:18:23 -04:00
}
2014-04-24 17:49:44 -04:00
2013-09-04 05:18:23 -04:00
}
2014-04-24 17:49:44 -04:00
2013-09-04 05:18:23 -04:00
return nil
}
2015-10-06 17:37:21 -04:00
// Cleanup deleted devices. It assumes that all the devices have been
2015-10-06 17:37:21 -04:00
// loaded in the hash table.
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) cleanupDeletedDevices ( ) error {
2015-10-06 17:37:21 -04:00
devices . Lock ( )
2015-10-06 17:37:21 -04:00
// If there are no deleted devices, there is nothing to do.
if devices . nrDeletedDevices == 0 {
2015-10-19 17:51:17 -04:00
devices . Unlock ( )
2015-10-06 17:37:21 -04:00
return nil
}
2015-10-06 17:37:21 -04:00
var deletedDevices [ ] * devInfo
for _ , info := range devices . Devices {
if ! info . Deleted {
continue
}
logrus . Debugf ( "devmapper: Found deleted device %s." , info . Hash )
deletedDevices = append ( deletedDevices , info )
}
// Delete the deleted devices. DeleteDevice() first takes the info lock
// and then devices.Lock(). So drop it to avoid deadlock.
devices . Unlock ( )
for _ , info := range deletedDevices {
// This will again try deferred deletion.
if err := devices . DeleteDevice ( info . Hash , false ) ; err != nil {
logrus . Warnf ( "devmapper: Deletion of device %s, device_id=%v failed:%v" , info . Hash , info . DeviceID , err )
}
}
return nil
}
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) countDeletedDevices ( ) {
for _ , info := range devices . Devices {
if ! info . Deleted {
continue
}
devices . nrDeletedDevices ++
}
}
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) startDeviceDeletionWorker ( ) {
// Deferred deletion is not enabled. Don't do anything.
if ! devices . deferredDelete {
return
}
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Worker to cleanup deleted devices started" )
2015-10-06 17:37:21 -04:00
for range devices . deletionWorkerTicker . C {
devices . cleanupDeletedDevices ( )
}
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) initMetaData ( ) error {
2015-10-01 13:02:55 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2014-12-03 13:06:43 -05:00
if err := devices . migrateOldMetaData ( ) ; err != nil {
return err
}
2015-07-22 20:42:28 -04:00
_ , transactionID , _ , _ , _ , _ , err := devices . poolStatus ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return err
}
2015-07-22 20:42:28 -04:00
devices . TransactionID = transactionID
2014-12-03 13:06:43 -05:00
2015-10-06 17:37:21 -04:00
if err := devices . loadDeviceFilesOnStart ( ) ; err != nil {
return fmt . Errorf ( "devmapper: Failed to load device files:%v" , err )
2014-12-03 13:06:43 -05:00
}
2014-12-03 13:06:43 -05:00
2015-10-06 17:37:21 -04:00
devices . constructDeviceIDMap ( )
2015-10-06 17:37:21 -04:00
devices . countDeletedDevices ( )
2015-10-06 17:37:21 -04:00
2014-12-03 13:06:43 -05:00
if err := devices . processPendingTransaction ( ) ; err != nil {
return err
}
2015-10-06 17:37:21 -04:00
2015-10-06 17:37:21 -04:00
// Start a goroutine to cleanup Deleted Devices
go devices . startDeviceDeletionWorker ( )
2014-12-03 13:06:43 -05:00
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) incNextDeviceID ( ) {
// IDs are 24bit, so wrap around
devices . NextDeviceID = ( devices . NextDeviceID + 1 ) & maxDeviceID
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) getNextFreeDeviceID ( ) ( int , error ) {
devices . incNextDeviceID ( )
for i := 0 ; i <= maxDeviceID ; i ++ {
if devices . isDeviceIDFree ( devices . NextDeviceID ) {
devices . markDeviceIDUsed ( devices . NextDeviceID )
return devices . NextDeviceID , nil
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
devices . incNextDeviceID ( )
2014-12-03 13:06:43 -05:00
}
2015-12-14 16:03:33 -05:00
return 0 , fmt . Errorf ( "devmapper: Unable to find a free device ID" )
2014-12-03 13:06:43 -05:00
}
2016-03-07 15:27:39 -05:00
func ( devices * DeviceSet ) poolHasFreeSpace ( ) error {
if devices . minFreeSpacePercent == 0 {
return nil
}
_ , _ , dataUsed , dataTotal , metadataUsed , metadataTotal , err := devices . poolStatus ( )
if err != nil {
return err
}
minFreeData := ( dataTotal * uint64 ( devices . minFreeSpacePercent ) ) / 100
if minFreeData < 1 {
minFreeData = 1
}
dataFree := dataTotal - dataUsed
if dataFree < minFreeData {
return fmt . Errorf ( "devmapper: Thin Pool has %v free data blocks which is less than minimum required %v free data blocks. Create more free space in thin pool or use dm.min_free_space option to change behavior" , ( dataTotal - dataUsed ) , minFreeData )
}
minFreeMetadata := ( metadataTotal * uint64 ( devices . minFreeSpacePercent ) ) / 100
if minFreeMetadata < 1 {
2016-03-17 11:19:08 -04:00
minFreeMetadata = 1
2016-03-07 15:27:39 -05:00
}
metadataFree := metadataTotal - metadataUsed
if metadataFree < minFreeMetadata {
return fmt . Errorf ( "devmapper: Thin Pool has %v free metadata blocks which is less than minimum required %v free metadata blocks. Create more free metadata space in thin pool or use dm.min_free_space option to change behavior" , ( metadataTotal - metadataUsed ) , minFreeMetadata )
}
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) createRegisterDevice ( hash string ) ( * devInfo , error ) {
2015-10-01 13:02:55 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2015-07-22 20:42:28 -04:00
deviceID , err := devices . getNextFreeDeviceID ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return nil , err
}
2015-07-22 20:42:28 -04:00
if err := devices . openTransaction ( hash , deviceID ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error opening transaction hash = %s deviceID = %d" , hash , deviceID )
2015-07-22 20:42:28 -04:00
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return nil , err
}
2014-12-03 13:06:43 -05:00
for {
2015-07-22 20:42:28 -04:00
if err := devicemapper . CreateDevice ( devices . getPoolDevName ( ) , deviceID ) ; err != nil {
2015-09-04 17:02:29 -04:00
if devicemapper . DeviceIDExists ( err ) {
2015-07-22 20:42:28 -04:00
// Device ID already exists. This should not
2015-12-13 11:00:39 -05:00
// happen. Now we have a mechanism to find
2015-07-22 20:42:28 -04:00
// a free device ID. So something is not right.
2014-12-03 13:06:43 -05:00
// Give a warning and continue.
2015-12-14 16:03:33 -05:00
logrus . Errorf ( "devmapper: Device ID %d exists in pool but it is supposed to be unused" , deviceID )
2015-07-22 20:42:28 -04:00
deviceID , err = devices . getNextFreeDeviceID ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return nil , err
}
2014-12-03 13:06:43 -05:00
// Save new device id into transaction
2015-07-22 20:42:28 -04:00
devices . refreshTransaction ( deviceID )
2014-12-03 13:06:43 -05:00
continue
}
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error creating device: %s" , err )
2015-07-22 20:42:28 -04:00
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return nil , err
2014-12-03 13:06:43 -05:00
}
break
}
2014-12-03 13:06:43 -05:00
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Registering device (id %v) with FS size %v" , deviceID , devices . baseFsSize )
2015-07-22 20:42:28 -04:00
info , err := devices . registerDevice ( deviceID , hash , devices . baseFsSize , devices . OpenTransactionID )
2014-12-03 13:06:43 -05:00
if err != nil {
2015-07-22 20:42:28 -04:00
_ = devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceID )
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return nil , err
}
2014-12-03 13:06:43 -05:00
if err := devices . closeTransaction ( ) ; err != nil {
2015-07-22 20:42:28 -04:00
devices . unregisterDevice ( deviceID , hash )
devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceID )
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return nil , err
}
return info , nil
}
2016-03-20 00:42:58 -04:00
func ( devices * DeviceSet ) createRegisterSnapDevice ( hash string , baseInfo * devInfo , size uint64 ) error {
2016-03-07 15:27:39 -05:00
if err := devices . poolHasFreeSpace ( ) ; err != nil {
return err
}
2015-07-22 20:42:28 -04:00
deviceID , err := devices . getNextFreeDeviceID ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return err
}
2015-07-22 20:42:28 -04:00
if err := devices . openTransaction ( hash , deviceID ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error opening transaction hash = %s deviceID = %d" , hash , deviceID )
2015-07-22 20:42:28 -04:00
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return err
}
2014-12-03 13:06:43 -05:00
for {
2015-07-22 20:42:28 -04:00
if err := devicemapper . CreateSnapDevice ( devices . getPoolDevName ( ) , deviceID , baseInfo . Name ( ) , baseInfo . DeviceID ) ; err != nil {
2015-09-04 17:02:29 -04:00
if devicemapper . DeviceIDExists ( err ) {
2015-07-22 20:42:28 -04:00
// Device ID already exists. This should not
2015-12-13 11:00:39 -05:00
// happen. Now we have a mechanism to find
2015-07-22 20:42:28 -04:00
// a free device ID. So something is not right.
2014-12-03 13:06:43 -05:00
// Give a warning and continue.
2015-12-14 16:03:33 -05:00
logrus . Errorf ( "devmapper: Device ID %d exists in pool but it is supposed to be unused" , deviceID )
2015-07-22 20:42:28 -04:00
deviceID , err = devices . getNextFreeDeviceID ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return err
}
2014-12-03 13:06:43 -05:00
// Save new device id into transaction
2015-07-22 20:42:28 -04:00
devices . refreshTransaction ( deviceID )
2014-12-03 13:06:43 -05:00
continue
}
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error creating snap device: %s" , err )
2015-07-22 20:42:28 -04:00
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return err
}
break
}
2014-12-03 13:06:43 -05:00
2016-03-20 00:42:58 -04:00
if _ , err := devices . registerDevice ( deviceID , hash , size , devices . OpenTransactionID ) ; err != nil {
2015-07-22 20:42:28 -04:00
devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceID )
devices . markDeviceIDFree ( deviceID )
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error registering device: %s" , err )
2014-12-03 13:06:43 -05:00
return err
}
2014-12-03 13:06:43 -05:00
if err := devices . closeTransaction ( ) ; err != nil {
2015-07-22 20:42:28 -04:00
devices . unregisterDevice ( deviceID , hash )
devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceID )
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return err
}
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) loadMetadata ( hash string ) * devInfo {
info := & devInfo { Hash : hash , devices : devices }
2014-04-24 17:49:44 -04:00
jsonData , err := ioutil . ReadFile ( devices . metadataFile ( info ) )
if err != nil {
2016-06-27 02:04:04 -04:00
logrus . Debugf ( "devmapper: Failed to read %s with err: %v" , devices . metadataFile ( info ) , err )
2014-04-24 17:49:44 -04:00
return nil
}
if err := json . Unmarshal ( jsonData , & info ) ; err != nil {
2016-06-27 02:04:04 -04:00
logrus . Debugf ( "devmapper: Failed to unmarshal devInfo from %s with err: %v" , devices . metadataFile ( info ) , err )
2014-04-24 17:49:44 -04:00
return nil
}
2015-09-30 18:54:06 -04:00
if info . DeviceID > maxDeviceID {
2015-12-14 16:03:33 -05:00
logrus . Errorf ( "devmapper: Ignoring Invalid DeviceId=%d" , info . DeviceID )
2015-09-30 18:54:06 -04:00
return nil
}
2014-04-24 17:49:44 -04:00
return info
}
2015-06-16 21:12:27 -04:00
func getDeviceUUID ( device string ) ( string , error ) {
out , err := exec . Command ( "blkid" , "-s" , "UUID" , "-o" , "value" , device ) . Output ( )
if err != nil {
2015-12-14 16:03:33 -05:00
return "" , fmt . Errorf ( "devmapper: Failed to find uuid for device %s:%v" , device , err )
2015-06-16 21:12:27 -04:00
}
uuid := strings . TrimSuffix ( string ( out ) , "\n" )
uuid = strings . TrimSpace ( uuid )
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: UUID for device: %s is:%s" , device , uuid )
2015-06-16 21:12:27 -04:00
return uuid , nil
}
2015-10-10 10:52:05 -04:00
func ( devices * DeviceSet ) getBaseDeviceSize ( ) uint64 {
info , _ := devices . lookupDevice ( "" )
if info == nil {
return 0
}
return info . Size
}
2015-11-13 13:56:21 -05:00
func ( devices * DeviceSet ) getBaseDeviceFS ( ) string {
2015-12-11 11:39:29 -05:00
return devices . BaseDeviceFilesystem
2015-11-13 13:56:21 -05:00
}
2015-11-11 12:07:35 -05:00
func ( devices * DeviceSet ) verifyBaseDeviceUUIDFS ( baseInfo * devInfo ) error {
2015-07-08 12:05:37 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2015-10-06 17:37:21 -04:00
if err := devices . activateDeviceIfNeeded ( baseInfo , false ) ; err != nil {
2015-06-16 21:12:27 -04:00
return err
}
defer devices . deactivateDevice ( baseInfo )
uuid , err := getDeviceUUID ( baseInfo . DevName ( ) )
if err != nil {
return err
}
if devices . BaseDeviceUUID != uuid {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Current Base Device UUID:%s does not match with stored UUID:%s. Possibly using a different thin pool than last invocation" , uuid , devices . BaseDeviceUUID )
2015-06-16 21:12:27 -04:00
}
2015-12-11 11:39:29 -05:00
if devices . BaseDeviceFilesystem == "" {
fsType , err := ProbeFsType ( baseInfo . DevName ( ) )
2015-11-11 12:07:35 -05:00
if err != nil {
return err
}
2015-12-11 11:39:29 -05:00
if err := devices . saveBaseDeviceFilesystem ( fsType ) ; err != nil {
return err
2015-11-11 12:07:35 -05:00
}
}
2015-12-11 11:39:29 -05:00
// If user specified a filesystem using dm.fs option and current
// file system of base image is not same, warn user that dm.fs
// will be ignored.
if devices . BaseDeviceFilesystem != devices . filesystem {
logrus . Warnf ( "devmapper: Base device already exists and has filesystem %s on it. User specified filesystem %s will be ignored." , devices . BaseDeviceFilesystem , devices . filesystem )
devices . filesystem = devices . BaseDeviceFilesystem
}
2015-06-16 21:12:27 -04:00
return nil
}
2015-12-11 11:39:29 -05:00
func ( devices * DeviceSet ) saveBaseDeviceFilesystem ( fs string ) error {
devices . BaseDeviceFilesystem = fs
return devices . saveDeviceSetMetaData ( )
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) saveBaseDeviceUUID ( baseInfo * devInfo ) error {
2015-07-08 12:05:37 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2015-10-06 17:37:21 -04:00
if err := devices . activateDeviceIfNeeded ( baseInfo , false ) ; err != nil {
2015-06-16 21:12:27 -04:00
return err
}
defer devices . deactivateDevice ( baseInfo )
uuid , err := getDeviceUUID ( baseInfo . DevName ( ) )
if err != nil {
return err
}
devices . BaseDeviceUUID = uuid
2015-12-11 11:39:29 -05:00
return devices . saveDeviceSetMetaData ( )
2015-06-16 21:12:27 -04:00
}
2015-10-06 08:38:03 -04:00
func ( devices * DeviceSet ) createBaseImage ( ) error {
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Initializing base device-mapper thin volume" )
2015-10-06 08:38:03 -04:00
// Create initial device
info , err := devices . createRegisterDevice ( "" )
if err != nil {
return err
}
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Creating filesystem on base device-mapper thin volume" )
2015-10-06 08:38:03 -04:00
2015-10-06 17:37:21 -04:00
if err := devices . activateDeviceIfNeeded ( info , false ) ; err != nil {
2015-10-06 08:38:03 -04:00
return err
}
if err := devices . createFilesystem ( info ) ; err != nil {
return err
}
info . Initialized = true
if err := devices . saveMetadata ( info ) ; err != nil {
info . Initialized = false
return err
}
if err := devices . saveBaseDeviceUUID ( info ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Could not query and save base device UUID:%v" , err )
2015-10-06 08:38:03 -04:00
}
return nil
}
2015-10-07 16:28:49 -04:00
// Returns if thin pool device exists or not. If device exists, also makes
// sure it is a thin pool device and not some other type of device.
func ( devices * DeviceSet ) thinPoolExists ( thinPoolDevice string ) ( bool , error ) {
logrus . Debugf ( "devmapper: Checking for existence of the pool %s" , thinPoolDevice )
info , err := devicemapper . GetInfo ( thinPoolDevice )
if err != nil {
return false , fmt . Errorf ( "devmapper: GetInfo() on device %s failed: %v" , thinPoolDevice , err )
}
// Device does not exist.
if info . Exists == 0 {
return false , nil
}
_ , _ , deviceType , _ , err := devicemapper . GetStatus ( thinPoolDevice )
if err != nil {
return false , fmt . Errorf ( "devmapper: GetStatus() on device %s failed: %v" , thinPoolDevice , err )
}
if deviceType != "thin-pool" {
return false , fmt . Errorf ( "devmapper: Device %s is not a thin pool" , thinPoolDevice )
}
return true , nil
}
2015-10-06 08:38:03 -04:00
func ( devices * DeviceSet ) checkThinPool ( ) error {
_ , transactionID , dataUsed , _ , _ , _ , err := devices . poolStatus ( )
if err != nil {
return err
}
if dataUsed != 0 {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to take ownership of thin-pool (%s) that already has used data blocks" ,
2015-10-06 08:38:03 -04:00
devices . thinPoolDevice )
}
if transactionID != 0 {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to take ownership of thin-pool (%s) with non-zero transaction ID" ,
2015-10-06 08:38:03 -04:00
devices . thinPoolDevice )
}
return nil
}
2015-10-06 08:38:03 -04:00
// Base image is initialized properly. Either save UUID for first time (for
// upgrade case or verify UUID.
2015-11-11 12:07:35 -05:00
func ( devices * DeviceSet ) setupVerifyBaseImageUUIDFS ( baseInfo * devInfo ) error {
2015-10-06 08:38:03 -04:00
// If BaseDeviceUUID is nil (upgrade case), save it and return success.
if devices . BaseDeviceUUID == "" {
if err := devices . saveBaseDeviceUUID ( baseInfo ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Could not query and save base device UUID:%v" , err )
2015-10-06 08:38:03 -04:00
}
return nil
}
2015-11-11 12:07:35 -05:00
if err := devices . verifyBaseDeviceUUIDFS ( baseInfo ) ; err != nil {
2016-05-29 05:51:06 -04:00
return fmt . Errorf ( "devmapper: Base Device UUID and Filesystem verification failed: %v" , err )
2015-10-06 08:38:03 -04:00
}
return nil
}
2016-01-12 13:44:13 -05:00
func ( devices * DeviceSet ) checkGrowBaseDeviceFS ( info * devInfo ) error {
if ! userBaseSize {
return nil
}
if devices . baseFsSize < devices . getBaseDeviceSize ( ) {
return fmt . Errorf ( "devmapper: Base device size cannot be smaller than %s" , units . HumanSize ( float64 ( devices . getBaseDeviceSize ( ) ) ) )
}
if devices . baseFsSize == devices . getBaseDeviceSize ( ) {
return nil
}
info . lock . Lock ( )
defer info . lock . Unlock ( )
devices . Lock ( )
defer devices . Unlock ( )
info . Size = devices . baseFsSize
if err := devices . saveMetadata ( info ) ; err != nil {
// Try to remove unused device
delete ( devices . Devices , info . Hash )
return err
}
return devices . growFS ( info )
}
func ( devices * DeviceSet ) growFS ( info * devInfo ) error {
if err := devices . activateDeviceIfNeeded ( info , false ) ; err != nil {
return fmt . Errorf ( "Error activating devmapper device: %s" , err )
}
defer devices . deactivateDevice ( info )
fsMountPoint := "/run/docker/mnt"
if _ , err := os . Stat ( fsMountPoint ) ; os . IsNotExist ( err ) {
if err := os . MkdirAll ( fsMountPoint , 0700 ) ; err != nil {
return err
}
defer os . RemoveAll ( fsMountPoint )
}
options := ""
if devices . BaseDeviceFilesystem == "xfs" {
// XFS needs nouuid or it can't mount filesystems with the same fs
options = joinMountOptions ( options , "nouuid" )
}
options = joinMountOptions ( options , devices . mountOptions )
if err := mount . Mount ( info . DevName ( ) , fsMountPoint , devices . BaseDeviceFilesystem , options ) ; err != nil {
return fmt . Errorf ( "Error mounting '%s' on '%s': %s" , info . DevName ( ) , fsMountPoint , err )
}
defer syscall . Unmount ( fsMountPoint , syscall . MNT_DETACH )
switch devices . BaseDeviceFilesystem {
case "ext4" :
if out , err := exec . Command ( "resize2fs" , info . DevName ( ) ) . CombinedOutput ( ) ; err != nil {
return fmt . Errorf ( "Failed to grow rootfs:%v:%s" , err , string ( out ) )
}
case "xfs" :
if out , err := exec . Command ( "xfs_growfs" , info . DevName ( ) ) . CombinedOutput ( ) ; err != nil {
return fmt . Errorf ( "Failed to grow rootfs:%v:%s" , err , string ( out ) )
}
default :
return fmt . Errorf ( "Unsupported filesystem type %s" , devices . BaseDeviceFilesystem )
}
return nil
}
2013-10-17 20:07:34 -04:00
func ( devices * DeviceSet ) setupBaseImage ( ) error {
2015-10-01 13:02:55 -04:00
oldInfo , _ := devices . lookupDeviceWithLock ( "" )
2015-10-06 08:38:03 -04:00
// base image already exists. If it is initialized properly, do UUID
// verification and return. Otherwise remove image and set it up
// fresh.
if oldInfo != nil {
2015-10-06 17:37:21 -04:00
if oldInfo . Initialized && ! oldInfo . Deleted {
2015-11-11 12:07:35 -05:00
if err := devices . setupVerifyBaseImageUUIDFS ( oldInfo ) ; err != nil {
2015-10-06 08:38:03 -04:00
return err
2015-06-16 21:12:27 -04:00
}
2015-12-11 11:39:29 -05:00
2016-01-12 13:44:13 -05:00
if err := devices . checkGrowBaseDeviceFS ( oldInfo ) ; err != nil {
return err
2015-11-10 01:50:19 -05:00
}
2015-12-11 11:39:29 -05:00
2015-10-06 08:38:03 -04:00
return nil
2015-06-16 21:12:27 -04:00
}
2013-09-04 05:18:23 -04:00
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Removing uninitialized base image" )
2015-10-06 17:37:21 -04:00
// If previous base device is in deferred delete state,
// that needs to be cleaned up first. So don't try
// deferred deletion.
if err := devices . DeleteDevice ( "" , true ) ; err != nil {
2013-09-04 05:18:23 -04:00
return err
}
}
2015-10-06 08:38:03 -04:00
// If we are setting up base image for the first time, make sure
// thin pool is empty.
2014-10-24 19:25:24 -04:00
if devices . thinPoolDevice != "" && oldInfo == nil {
2015-10-06 08:38:03 -04:00
if err := devices . checkThinPool ( ) ; err != nil {
2014-10-24 19:25:24 -04:00
return err
2014-11-21 22:26:09 -05:00
}
2014-10-24 19:25:24 -04:00
}
2015-10-06 08:38:03 -04:00
// Create new base image device
if err := devices . createBaseImage ( ) ; err != nil {
2013-09-04 05:18:23 -04:00
return err
}
return nil
}
2013-10-03 15:00:16 -04:00
func setCloseOnExec ( name string ) {
2013-11-20 16:05:17 -05:00
if fileInfos , _ := ioutil . ReadDir ( "/proc/self/fd" ) ; fileInfos != nil {
2013-10-03 15:00:16 -04:00
for _ , i := range fileInfos {
2014-05-16 08:10:02 -04:00
link , _ := os . Readlink ( filepath . Join ( "/proc/self/fd" , i . Name ( ) ) )
2013-10-11 23:37:11 -04:00
if link == name {
2013-10-03 15:00:16 -04:00
fd , err := strconv . Atoi ( i . Name ( ) )
if err == nil {
2014-05-16 08:10:02 -04:00
syscall . CloseOnExec ( fd )
2013-10-03 15:00:16 -04:00
}
}
}
}
}
2015-07-22 20:42:28 -04:00
// DMLog implements logging using DevMapperLogger interface.
2014-11-05 18:10:38 -05:00
func ( devices * DeviceSet ) DMLog ( level int , file string , line int , dmError int , message string ) {
2015-04-02 16:47:14 -04:00
// By default libdm sends us all the messages including debug ones.
// We need to filter out messages here and figure out which one
// should be printed.
2015-07-22 20:42:28 -04:00
if level > logLevel {
2015-04-02 16:47:14 -04:00
return
2013-10-10 09:24:39 -04:00
}
2014-11-05 18:10:38 -05:00
// FIXME(vbatts) push this back into ./pkg/devicemapper/
2015-04-02 16:47:14 -04:00
if level <= devicemapper . LogLevelErr {
logrus . Errorf ( "libdevmapper(%d): %s:%d (%d) %s" , level , file , line , dmError , message )
} else if level <= devicemapper . LogLevelInfo {
logrus . Infof ( "libdevmapper(%d): %s:%d (%d) %s" , level , file , line , dmError , message )
} else {
// FIXME(vbatts) push this back into ./pkg/devicemapper/
logrus . Debugf ( "libdevmapper(%d): %s:%d (%d) %s" , level , file , line , dmError , message )
}
2013-10-10 09:24:39 -04:00
}
2013-11-07 18:58:03 -05:00
func major ( device uint64 ) uint64 {
return ( device >> 8 ) & 0xfff
2013-10-18 05:38:21 -04:00
}
2013-11-07 18:58:03 -05:00
func minor ( device uint64 ) uint64 {
2013-10-18 05:38:21 -04:00
return ( device & 0xff ) | ( ( device >> 12 ) & 0xfff00 )
}
2015-07-22 20:42:28 -04:00
// ResizePool increases the size of the pool.
2013-11-18 06:12:04 -05:00
func ( devices * DeviceSet ) ResizePool ( size int64 ) error {
dirname := devices . loopbackDir ( )
datafilename := path . Join ( dirname , "data" )
2014-09-22 17:03:31 -04:00
if len ( devices . dataDevice ) > 0 {
datafilename = devices . dataDevice
}
2013-11-18 06:12:04 -05:00
metadatafilename := path . Join ( dirname , "metadata" )
2014-09-22 17:03:31 -04:00
if len ( devices . metadataDevice ) > 0 {
metadatafilename = devices . metadataDevice
}
2013-11-18 06:12:04 -05:00
2014-05-16 08:10:02 -04:00
datafile , err := os . OpenFile ( datafilename , os . O_RDWR , 0 )
2013-11-18 06:12:04 -05:00
if datafile == nil {
return err
}
defer datafile . Close ( )
fi , err := datafile . Stat ( )
if fi == nil {
return err
}
if fi . Size ( ) > size {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Can't shrink file" )
2013-11-18 06:12:04 -05:00
}
2015-12-14 17:16:34 -05:00
dataloopback := loopback . FindLoopDeviceFor ( datafile )
2013-11-18 06:12:04 -05:00
if dataloopback == nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to find loopback mount for: %s" , datafilename )
2013-11-18 06:12:04 -05:00
}
defer dataloopback . Close ( )
2014-05-16 08:10:02 -04:00
metadatafile , err := os . OpenFile ( metadatafilename , os . O_RDWR , 0 )
2013-11-18 06:12:04 -05:00
if metadatafile == nil {
return err
}
defer metadatafile . Close ( )
2015-12-14 17:16:34 -05:00
metadataloopback := loopback . FindLoopDeviceFor ( metadatafile )
2013-11-18 06:12:04 -05:00
if metadataloopback == nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to find loopback mount for: %s" , metadatafilename )
2013-11-18 06:12:04 -05:00
}
defer metadataloopback . Close ( )
// Grow loopback file
if err := datafile . Truncate ( size ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to grow loopback file: %s" , err )
2013-11-18 06:12:04 -05:00
}
// Reload size for loopback device
2015-12-14 17:16:34 -05:00
if err := loopback . SetCapacity ( dataloopback ) ; err != nil {
return fmt . Errorf ( "Unable to update loopback capacity: %s" , err )
2013-11-18 06:12:04 -05:00
}
// Suspend the pool
2014-11-05 18:10:38 -05:00
if err := devicemapper . SuspendDevice ( devices . getPoolName ( ) ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to suspend pool: %s" , err )
2013-11-18 06:12:04 -05:00
}
// Reload with the new block sizes
2014-11-05 18:10:38 -05:00
if err := devicemapper . ReloadPool ( devices . getPoolName ( ) , dataloopback , metadataloopback , devices . thinpBlockSize ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to reload pool: %s" , err )
2013-11-18 06:12:04 -05:00
}
// Resume the pool
2014-11-05 18:10:38 -05:00
if err := devicemapper . ResumeDevice ( devices . getPoolName ( ) ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Unable to resume pool: %s" , err )
2013-11-18 06:12:04 -05:00
}
return nil
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) loadTransactionMetaData ( ) error {
jsonData , err := ioutil . ReadFile ( devices . transactionMetaFile ( ) )
if err != nil {
// There is no active transaction. This will be the case
// during upgrade.
if os . IsNotExist ( err ) {
2015-07-22 20:42:28 -04:00
devices . OpenTransactionID = devices . TransactionID
2014-12-03 13:06:43 -05:00
return nil
}
return err
}
2015-07-22 20:42:28 -04:00
json . Unmarshal ( jsonData , & devices . transaction )
2014-12-03 13:06:43 -05:00
return nil
}
func ( devices * DeviceSet ) saveTransactionMetaData ( ) error {
2015-07-22 20:42:28 -04:00
jsonData , err := json . Marshal ( & devices . transaction )
2014-12-03 13:06:43 -05:00
if err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error encoding metadata to json: %s" , err )
2014-12-03 13:06:43 -05:00
}
return devices . writeMetaFile ( jsonData , devices . transactionMetaFile ( ) )
}
func ( devices * DeviceSet ) removeTransactionMetaData ( ) error {
if err := os . RemoveAll ( devices . transactionMetaFile ( ) ) ; err != nil {
return err
}
return nil
}
func ( devices * DeviceSet ) rollbackTransaction ( ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Rolling back open transaction: TransactionID=%d hash=%s device_id=%d" , devices . OpenTransactionID , devices . DeviceIDHash , devices . DeviceID )
2014-12-03 13:06:43 -05:00
// A device id might have already been deleted before transaction
// closed. In that case this call will fail. Just leave a message
// in case of failure.
2015-07-22 20:42:28 -04:00
if err := devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , devices . DeviceID ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Errorf ( "devmapper: Unable to delete device: %s" , err )
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
dinfo := & devInfo { Hash : devices . DeviceIDHash }
2014-12-03 13:06:43 -05:00
if err := devices . removeMetadata ( dinfo ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Errorf ( "devmapper: Unable to remove metadata: %s" , err )
2014-12-03 13:06:43 -05:00
} else {
2015-07-22 20:42:28 -04:00
devices . markDeviceIDFree ( devices . DeviceID )
2014-12-03 13:06:43 -05:00
}
if err := devices . removeTransactionMetaData ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Errorf ( "devmapper: Unable to remove transaction meta file %s: %s" , devices . transactionMetaFile ( ) , err )
2014-12-03 13:06:43 -05:00
}
return nil
}
func ( devices * DeviceSet ) processPendingTransaction ( ) error {
if err := devices . loadTransactionMetaData ( ) ; err != nil {
return err
}
2015-07-22 20:42:28 -04:00
// If there was open transaction but pool transaction ID is same
// as open transaction ID, nothing to roll back.
if devices . TransactionID == devices . OpenTransactionID {
2014-12-03 13:06:43 -05:00
return nil
}
2015-07-22 20:42:28 -04:00
// If open transaction ID is less than pool transaction ID, something
2014-12-03 13:06:43 -05:00
// is wrong. Bail out.
2015-07-22 20:42:28 -04:00
if devices . OpenTransactionID < devices . TransactionID {
2015-12-14 16:03:33 -05:00
logrus . Errorf ( "devmapper: Open Transaction id %d is less than pool transaction id %d" , devices . OpenTransactionID , devices . TransactionID )
2014-12-03 13:06:43 -05:00
return nil
}
2015-07-22 20:42:28 -04:00
// Pool transaction ID is not same as open transaction. There is
2014-12-03 13:06:43 -05:00
// a transaction which was not completed.
if err := devices . rollbackTransaction ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Rolling back open transaction failed: %s" , err )
2014-12-03 13:06:43 -05:00
}
2015-07-22 20:42:28 -04:00
devices . OpenTransactionID = devices . TransactionID
2014-12-03 13:06:43 -05:00
return nil
}
2014-11-05 09:25:02 -05:00
func ( devices * DeviceSet ) loadDeviceSetMetaData ( ) error {
jsonData , err := ioutil . ReadFile ( devices . deviceSetMetaFile ( ) )
if err != nil {
2014-11-07 16:53:34 -05:00
// For backward compatibility return success if file does
// not exist.
if os . IsNotExist ( err ) {
return nil
}
return err
2014-11-05 09:25:02 -05:00
}
2014-11-07 16:53:34 -05:00
return json . Unmarshal ( jsonData , devices )
2014-11-05 09:25:02 -05:00
}
func ( devices * DeviceSet ) saveDeviceSetMetaData ( ) error {
jsonData , err := json . Marshal ( devices )
if err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error encoding metadata to json: %s" , err )
2014-11-05 09:25:02 -05:00
}
2014-11-07 16:53:34 -05:00
return devices . writeMetaFile ( jsonData , devices . deviceSetMetaFile ( ) )
2014-11-05 09:25:02 -05:00
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) openTransaction ( hash string , DeviceID int ) error {
devices . allocateTransactionID ( )
devices . DeviceIDHash = hash
devices . DeviceID = DeviceID
2014-12-03 13:06:43 -05:00
if err := devices . saveTransactionMetaData ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error saving transaction metadata: %s" , err )
2014-12-03 13:06:43 -05:00
}
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) refreshTransaction ( DeviceID int ) error {
devices . DeviceID = DeviceID
2014-12-03 13:06:43 -05:00
if err := devices . saveTransactionMetaData ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error saving transaction metadata: %s" , err )
2014-12-03 13:06:43 -05:00
}
return nil
}
func ( devices * DeviceSet ) closeTransaction ( ) error {
2015-07-22 20:42:28 -04:00
if err := devices . updatePoolTransactionID ( ) ; err != nil {
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Failed to close Transaction" )
2014-12-03 13:06:43 -05:00
return err
}
return nil
}
2015-04-21 18:14:59 -04:00
func determineDriverCapabilities ( version string ) error {
/ *
* Driver version 4.27 .0 and greater support deferred activation
* feature .
* /
logrus . Debugf ( "devicemapper: driver version is %s" , version )
versionSplit := strings . Split ( version , "." )
major , err := strconv . Atoi ( versionSplit [ 0 ] )
if err != nil {
return graphdriver . ErrNotSupported
}
if major > 4 {
2015-07-22 20:42:28 -04:00
driverDeferredRemovalSupport = true
2015-04-21 18:14:59 -04:00
return nil
}
if major < 4 {
return nil
}
minor , err := strconv . Atoi ( versionSplit [ 1 ] )
if err != nil {
return graphdriver . ErrNotSupported
}
/ *
* If major is 4 and minor is 27 , then there is no need to
* check for patch level as it can not be less than 0.
* /
if minor >= 27 {
2015-07-22 20:42:28 -04:00
driverDeferredRemovalSupport = true
2015-04-21 18:14:59 -04:00
return nil
}
return nil
}
2015-07-07 14:13:29 -04:00
// Determine the major and minor number of loopback device
func getDeviceMajorMinor ( file * os . File ) ( uint64 , uint64 , error ) {
stat , err := file . Stat ( )
if err != nil {
return 0 , 0 , err
}
dev := stat . Sys ( ) . ( * syscall . Stat_t ) . Rdev
majorNum := major ( dev )
minorNum := minor ( dev )
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Major:Minor for device: %s is:%v:%v" , file . Name ( ) , majorNum , minorNum )
2015-07-07 14:13:29 -04:00
return majorNum , minorNum , nil
}
// Given a file which is backing file of a loop back device, find the
// loopback device name and its major/minor number.
func getLoopFileDeviceMajMin ( filename string ) ( string , uint64 , uint64 , error ) {
file , err := os . Open ( filename )
if err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Failed to open file %s" , filename )
2015-07-07 14:13:29 -04:00
return "" , 0 , 0 , err
}
defer file . Close ( )
2015-12-14 17:16:34 -05:00
loopbackDevice := loopback . FindLoopDeviceFor ( file )
2015-07-07 14:13:29 -04:00
if loopbackDevice == nil {
2015-12-14 16:03:33 -05:00
return "" , 0 , 0 , fmt . Errorf ( "devmapper: Unable to find loopback mount for: %s" , filename )
2015-07-07 14:13:29 -04:00
}
defer loopbackDevice . Close ( )
Major , Minor , err := getDeviceMajorMinor ( loopbackDevice )
if err != nil {
return "" , 0 , 0 , err
}
return loopbackDevice . Name ( ) , Major , Minor , nil
}
// Get the major/minor numbers of thin pool data and metadata devices
func ( devices * DeviceSet ) getThinPoolDataMetaMajMin ( ) ( uint64 , uint64 , uint64 , uint64 , error ) {
var params , poolDataMajMin , poolMetadataMajMin string
_ , _ , _ , params , err := devicemapper . GetTable ( devices . getPoolName ( ) )
if err != nil {
return 0 , 0 , 0 , 0 , err
}
if _ , err = fmt . Sscanf ( params , "%s %s" , & poolMetadataMajMin , & poolDataMajMin ) ; err != nil {
return 0 , 0 , 0 , 0 , err
}
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: poolDataMajMin=%s poolMetaMajMin=%s\n" , poolDataMajMin , poolMetadataMajMin )
2015-07-07 14:13:29 -04:00
poolDataMajMinorSplit := strings . Split ( poolDataMajMin , ":" )
poolDataMajor , err := strconv . ParseUint ( poolDataMajMinorSplit [ 0 ] , 10 , 32 )
if err != nil {
return 0 , 0 , 0 , 0 , err
}
poolDataMinor , err := strconv . ParseUint ( poolDataMajMinorSplit [ 1 ] , 10 , 32 )
if err != nil {
return 0 , 0 , 0 , 0 , err
}
poolMetadataMajMinorSplit := strings . Split ( poolMetadataMajMin , ":" )
poolMetadataMajor , err := strconv . ParseUint ( poolMetadataMajMinorSplit [ 0 ] , 10 , 32 )
if err != nil {
return 0 , 0 , 0 , 0 , err
}
poolMetadataMinor , err := strconv . ParseUint ( poolMetadataMajMinorSplit [ 1 ] , 10 , 32 )
if err != nil {
return 0 , 0 , 0 , 0 , err
}
return poolDataMajor , poolDataMinor , poolMetadataMajor , poolMetadataMinor , nil
}
func ( devices * DeviceSet ) loadThinPoolLoopBackInfo ( ) error {
poolDataMajor , poolDataMinor , poolMetadataMajor , poolMetadataMinor , err := devices . getThinPoolDataMetaMajMin ( )
if err != nil {
return err
}
dirname := devices . loopbackDir ( )
// data device has not been passed in. So there should be a data file
// which is being mounted as loop device.
if devices . dataDevice == "" {
datafilename := path . Join ( dirname , "data" )
dataLoopDevice , dataMajor , dataMinor , err := getLoopFileDeviceMajMin ( datafilename )
if err != nil {
return err
}
// Compare the two
if poolDataMajor == dataMajor && poolDataMinor == dataMinor {
devices . dataDevice = dataLoopDevice
devices . dataLoopFile = datafilename
}
}
// metadata device has not been passed in. So there should be a
// metadata file which is being mounted as loop device.
if devices . metadataDevice == "" {
metadatafilename := path . Join ( dirname , "metadata" )
metadataLoopDevice , metadataMajor , metadataMinor , err := getLoopFileDeviceMajMin ( metadatafilename )
if err != nil {
return err
}
if poolMetadataMajor == metadataMajor && poolMetadataMinor == metadataMinor {
devices . metadataDevice = metadataLoopDevice
devices . metadataLoopFile = metadatafilename
}
}
return nil
}
2016-06-13 12:05:46 -04:00
func ( devices * DeviceSet ) enableDeferredRemovalDeletion ( ) error {
2015-04-21 18:14:59 -04:00
2015-10-06 17:37:21 -04:00
// If user asked for deferred removal then check both libdm library
// and kernel driver support deferred removal otherwise error out.
if enableDeferredRemoval {
if ! driverDeferredRemovalSupport {
return fmt . Errorf ( "devmapper: Deferred removal can not be enabled as kernel does not support it" )
}
if ! devicemapper . LibraryDeferredRemovalSupport {
return fmt . Errorf ( "devmapper: Deferred removal can not be enabled as libdm does not support it" )
}
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Deferred removal support enabled." )
2015-04-21 18:14:59 -04:00
devices . deferredRemove = true
}
2015-10-06 17:37:21 -04:00
if enableDeferredDeletion {
if ! devices . deferredRemove {
return fmt . Errorf ( "devmapper: Deferred deletion can not be enabled as deferred removal is not enabled. Enable deferred removal using --storage-opt dm.use_deferred_removal=true parameter" )
}
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Deferred deletion support enabled." )
2015-10-06 17:37:21 -04:00
devices . deferredDelete = true
}
2016-06-13 12:05:46 -04:00
return nil
}
func ( devices * DeviceSet ) initDevmapper ( doInit bool ) error {
// give ourselves to libdm as a log handler
devicemapper . LogInit ( devices )
version , err := devicemapper . GetDriverVersion ( )
if err != nil {
// Can't even get driver version, assume not supported
return graphdriver . ErrNotSupported
}
if err := determineDriverCapabilities ( version ) ; err != nil {
return graphdriver . ErrNotSupported
}
if err := devices . enableDeferredRemovalDeletion ( ) ; err != nil {
return err
}
2015-10-06 17:37:21 -04:00
2015-01-19 15:11:40 -05:00
// https://github.com/docker/docker/issues/4036
if supported := devicemapper . UdevSetSyncSupport ( true ) ; ! supported {
2016-04-01 16:31:44 -04:00
if dockerversion . IAmStatic == "true" {
logrus . Errorf ( "devmapper: Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a dynamic binary to use devicemapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/daemon/#daemon-storage-driver-option" )
} else {
logrus . Errorf ( "devmapper: Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a more recent version of libdevmapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/daemon/#daemon-storage-driver-option" )
}
2016-03-10 13:13:44 -05:00
if ! devices . overrideUdevSyncCheck {
return graphdriver . ErrNotSupported
}
2015-01-19 15:11:40 -05:00
}
2015-10-08 11:51:41 -04:00
//create the root dir of the devmapper driver ownership to match this
//daemon's remapped root uid/gid so containers can start properly
uid , gid , err := idtools . GetRootUIDGID ( devices . uidMaps , devices . gidMaps )
if err != nil {
return err
}
if err := idtools . MkdirAs ( devices . root , 0700 , uid , gid ) ; err != nil && ! os . IsExist ( err ) {
return err
}
if err := os . MkdirAll ( devices . metadataDir ( ) , 0700 ) ; err != nil && ! os . IsExist ( err ) {
2013-09-04 05:18:23 -04:00
return err
}
2013-10-17 03:53:09 -04:00
// Set the device prefix from the device id and inode of the docker root dir
2013-10-16 21:46:28 -04:00
2014-05-16 08:10:02 -04:00
st , err := os . Stat ( devices . root )
2013-09-04 05:18:23 -04:00
if err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error looking up dir %s: %s" , devices . root , err )
2013-09-04 05:18:23 -04:00
}
2014-05-16 08:10:02 -04:00
sysSt := st . Sys ( ) . ( * syscall . Stat_t )
2013-10-16 21:46:28 -04:00
// "reg-" stands for "regular file".
// In the future we might use "dev-" for "device file", etc.
2013-10-18 04:30:53 -04:00
// docker-maj,min[-inode] stands for:
// - Managed by docker
// - The target of this device is at major <maj> and minor <min>
// - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself.
2013-10-18 05:39:47 -04:00
devices . devicePrefix = fmt . Sprintf ( "docker-%d:%d-%d" , major ( sysSt . Dev ) , minor ( sysSt . Dev ) , sysSt . Ino )
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Generated prefix: %s" , devices . devicePrefix )
2013-10-16 21:46:28 -04:00
2014-10-24 19:25:24 -04:00
// Check for the existence of the thin-pool device
2015-10-07 16:28:49 -04:00
poolExists , err := devices . thinPoolExists ( devices . getPoolName ( ) )
if err != nil {
2013-09-04 05:18:23 -04:00
return err
}
2013-10-16 21:46:28 -04:00
// It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files
2015-11-04 14:39:12 -05:00
// that are not Close-on-exec,
2013-10-16 21:46:28 -04:00
// so we add this badhack to make sure it closes itself
setCloseOnExec ( "/dev/mapper/control" )
2014-04-24 17:49:44 -04:00
// Make sure the sparse images exist in <root>/devicemapper/data and
// <root>/devicemapper/metadata
createdLoopback := false
2013-10-16 21:46:28 -04:00
// If the pool doesn't exist, create it
2015-10-07 16:28:49 -04:00
if ! poolExists && devices . thinPoolDevice == "" {
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Pool doesn't exist. Creating it." )
2013-10-17 18:04:14 -04:00
2014-03-28 10:36:55 -04:00
var (
dataFile * os . File
metadataFile * os . File
)
2014-04-24 17:49:44 -04:00
2014-03-28 10:36:55 -04:00
if devices . dataDevice == "" {
// Make sure the sparse images exist in <root>/devicemapper/data
2014-04-24 17:49:44 -04:00
2014-03-28 10:36:55 -04:00
hasData := devices . hasImage ( "data" )
2014-04-24 17:49:44 -04:00
2014-03-28 10:36:55 -04:00
if ! doInit && ! hasData {
return errors . New ( "Loopback data file not found" )
}
2014-04-24 17:49:44 -04:00
2014-03-28 10:36:55 -04:00
if ! hasData {
createdLoopback = true
}
data , err := devices . ensureImage ( "data" , devices . dataLoopbackSize )
if err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error device ensureImage (data): %s" , err )
2014-03-28 10:36:55 -04:00
return err
}
2015-12-14 17:16:34 -05:00
dataFile , err = loopback . AttachLoopDevice ( data )
2014-03-28 10:36:55 -04:00
if err != nil {
return err
}
2015-01-09 11:20:07 -05:00
devices . dataLoopFile = data
devices . dataDevice = dataFile . Name ( )
2014-03-28 10:36:55 -04:00
} else {
dataFile , err = os . OpenFile ( devices . dataDevice , os . O_RDWR , 0600 )
if err != nil {
return err
}
2013-10-16 21:46:28 -04:00
}
2014-06-05 18:13:43 -04:00
defer dataFile . Close ( )
2013-10-16 21:46:28 -04:00
2014-03-28 10:36:55 -04:00
if devices . metadataDevice == "" {
// Make sure the sparse images exist in <root>/devicemapper/metadata
hasMetadata := devices . hasImage ( "metadata" )
if ! doInit && ! hasMetadata {
return errors . New ( "Loopback metadata file not found" )
}
if ! hasMetadata {
createdLoopback = true
}
metadata , err := devices . ensureImage ( "metadata" , devices . metaDataLoopbackSize )
if err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error device ensureImage (metadata): %s" , err )
2014-03-28 10:36:55 -04:00
return err
}
2015-12-14 17:16:34 -05:00
metadataFile , err = loopback . AttachLoopDevice ( metadata )
2014-03-28 10:36:55 -04:00
if err != nil {
return err
}
2015-01-09 11:20:07 -05:00
devices . metadataLoopFile = metadata
devices . metadataDevice = metadataFile . Name ( )
2014-03-28 10:36:55 -04:00
} else {
metadataFile , err = os . OpenFile ( devices . metadataDevice , os . O_RDWR , 0600 )
if err != nil {
return err
}
2013-10-16 21:46:28 -04:00
}
2014-06-05 18:13:43 -04:00
defer metadataFile . Close ( )
2013-10-16 21:46:28 -04:00
2014-11-05 18:10:38 -05:00
if err := devicemapper . CreatePool ( devices . getPoolName ( ) , dataFile , metadataFile , devices . thinpBlockSize ) ; err != nil {
2013-10-16 21:46:28 -04:00
return err
}
2013-09-04 05:18:23 -04:00
}
2015-07-07 14:13:29 -04:00
// Pool already exists and caller did not pass us a pool. That means
// we probably created pool earlier and could not remove it as some
// containers were still using it. Detect some of the properties of
// pool, like is it using loop devices.
2015-10-07 16:28:49 -04:00
if poolExists && devices . thinPoolDevice == "" {
2015-07-07 14:13:29 -04:00
if err := devices . loadThinPoolLoopBackInfo ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Failed to load thin pool loopback device information:%v" , err )
2015-07-07 14:13:29 -04:00
return err
}
}
2013-10-16 21:46:28 -04:00
// If we didn't just create the data or metadata image, we need to
2014-04-24 17:49:44 -04:00
// load the transaction id and migrate old metadata
2013-09-04 05:18:23 -04:00
if ! createdLoopback {
2015-04-26 12:50:25 -04:00
if err := devices . initMetaData ( ) ; err != nil {
2013-09-04 05:18:23 -04:00
return err
}
}
2015-08-04 14:33:00 -04:00
if devices . thinPoolDevice == "" {
if devices . metadataLoopFile != "" || devices . dataLoopFile != "" {
2016-06-11 13:42:38 -04:00
logrus . Warn ( "devmapper: Usage of loopback devices is strongly discouraged for production use. Please use `--storage-opt dm.thinpooldev` or use `man docker` to refer to dm.thinpooldev section." )
2015-08-04 14:33:00 -04:00
}
}
2015-07-22 20:42:28 -04:00
// Right now this loads only NextDeviceID. If there is more metadata
2014-11-05 09:25:02 -05:00
// down the line, we might have to move it earlier.
2015-04-26 12:50:25 -04:00
if err := devices . loadDeviceSetMetaData ( ) ; err != nil {
2014-11-07 16:53:34 -05:00
return err
}
2014-11-05 09:25:02 -05:00
2013-10-16 21:46:28 -04:00
// Setup the base image
2013-11-18 05:33:07 -05:00
if doInit {
if err := devices . setupBaseImage ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error device setupBaseImage: %s" , err )
2013-11-18 05:33:07 -05:00
return err
}
2013-09-04 05:18:23 -04:00
}
return nil
}
2015-07-22 20:42:28 -04:00
// AddDevice adds a device and registers in the hash.
2016-03-20 00:42:58 -04:00
func ( devices * DeviceSet ) AddDevice ( hash , baseHash string , storageOpt map [ string ] string ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: AddDevice(hash=%s basehash=%s)" , hash , baseHash )
defer logrus . Debugf ( "devmapper: AddDevice(hash=%s basehash=%s) END" , hash , baseHash )
2014-12-03 13:06:43 -05:00
2015-10-06 17:37:21 -04:00
// If a deleted device exists, return error.
2015-10-01 13:02:55 -04:00
baseInfo , err := devices . lookupDeviceWithLock ( baseHash )
2014-04-01 04:45:40 -04:00
if err != nil {
return err
2013-09-04 05:18:23 -04:00
}
2015-10-06 17:37:21 -04:00
if baseInfo . Deleted {
return fmt . Errorf ( "devmapper: Base device %v has been marked for deferred deletion" , baseInfo . Hash )
}
2014-03-06 12:14:56 -05:00
baseInfo . lock . Lock ( )
defer baseInfo . lock . Unlock ( )
2014-04-01 05:28:21 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2015-10-06 17:37:21 -04:00
// Also include deleted devices in case hash of new device is
// same as one of the deleted devices.
2014-04-01 05:28:21 -04:00
if info , _ := devices . lookupDevice ( hash ) ; info != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: device %s already exists. Deleted=%v" , hash , info . Deleted )
2014-04-01 05:28:21 -04:00
}
2016-04-01 15:05:09 -04:00
size , err := devices . parseStorageOpt ( storageOpt )
if err != nil {
2013-09-04 05:18:23 -04:00
return err
}
2014-12-03 13:06:43 -05:00
2016-04-01 15:05:09 -04:00
if size == 0 {
size = baseInfo . Size
2016-03-20 00:42:58 -04:00
}
2016-04-01 15:05:09 -04:00
if size < baseInfo . Size {
2016-03-20 00:42:58 -04:00
return fmt . Errorf ( "devmapper: Container size cannot be smaller than %s" , units . HumanSize ( float64 ( baseInfo . Size ) ) )
}
2016-04-01 15:05:09 -04:00
if err := devices . createRegisterSnapDevice ( hash , baseInfo , size ) ; err != nil {
2016-03-20 00:42:58 -04:00
return err
}
// Grow the container rootfs.
2016-04-11 21:27:47 -04:00
if size > baseInfo . Size {
2016-03-20 00:42:58 -04:00
info , err := devices . lookupDevice ( hash )
if err != nil {
return err
}
if err := devices . growFS ( info ) ; err != nil {
return err
}
}
return nil
}
2016-04-01 15:05:09 -04:00
func ( devices * DeviceSet ) parseStorageOpt ( storageOpt map [ string ] string ) ( uint64 , error ) {
2016-03-20 00:42:58 -04:00
// Read size to change the block device size per container.
for key , val := range storageOpt {
key := strings . ToLower ( key )
switch key {
case "size" :
size , err := units . RAMInBytes ( val )
if err != nil {
2016-04-01 15:05:09 -04:00
return 0 , err
2016-03-20 00:42:58 -04:00
}
2016-04-01 15:05:09 -04:00
return uint64 ( size ) , nil
2016-03-20 00:42:58 -04:00
default :
2016-04-01 15:05:09 -04:00
return 0 , fmt . Errorf ( "Unknown option %s" , key )
2016-03-20 00:42:58 -04:00
}
}
2016-04-01 15:05:09 -04:00
return 0 , nil
2013-09-04 05:18:23 -04:00
}
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) markForDeferredDeletion ( info * devInfo ) error {
// If device is already in deleted state, there is nothing to be done.
if info . Deleted {
return nil
}
logrus . Debugf ( "devmapper: Marking device %s for deferred deletion." , info . Hash )
info . Deleted = true
2015-12-13 11:00:39 -05:00
// save device metadata to reflect deleted state.
2015-10-06 17:37:21 -04:00
if err := devices . saveMetadata ( info ) ; err != nil {
info . Deleted = false
return err
}
2015-10-06 17:37:21 -04:00
devices . nrDeletedDevices ++
2015-10-06 17:37:21 -04:00
return nil
}
2015-12-13 11:00:39 -05:00
// Should be called with devices.Lock() held.
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) deleteTransaction ( info * devInfo , syncDelete bool ) error {
2015-10-05 09:02:31 -04:00
if err := devices . openTransaction ( info . Hash , info . DeviceID ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error opening transaction hash = %s deviceId = %d" , "" , info . DeviceID )
2015-10-05 09:02:31 -04:00
return err
2013-12-17 03:12:44 -05:00
}
2015-10-05 09:02:31 -04:00
defer devices . closeTransaction ( )
2015-10-06 17:37:21 -04:00
err := devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , info . DeviceID )
if err != nil {
// If syncDelete is true, we want to return error. If deferred
// deletion is not enabled, we return an error. If error is
// something other then EBUSY, return an error.
if syncDelete || ! devices . deferredDelete || err != devicemapper . ErrBusy {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error deleting device: %s" , err )
2015-10-06 17:37:21 -04:00
return err
}
2013-09-04 05:18:23 -04:00
}
2015-10-06 17:37:21 -04:00
if err == nil {
if err := devices . unregisterDevice ( info . DeviceID , info . Hash ) ; err != nil {
return err
}
2015-10-06 17:37:21 -04:00
// If device was already in deferred delete state that means
// deletion was being tried again later. Reduce the deleted
// device count.
if info . Deleted {
devices . nrDeletedDevices --
}
2016-01-11 13:57:37 -05:00
devices . markDeviceIDFree ( info . DeviceID )
2015-10-06 17:37:21 -04:00
} else {
if err := devices . markForDeferredDeletion ( info ) ; err != nil {
return err
}
2014-12-03 13:06:43 -05:00
}
2015-10-05 09:02:31 -04:00
return nil
}
// Issue discard only if device open count is zero.
func ( devices * DeviceSet ) issueDiscard ( info * devInfo ) error {
logrus . Debugf ( "devmapper: issueDiscard(device: %s). START" , info . Hash )
defer logrus . Debugf ( "devmapper: issueDiscard(device: %s). END" , info . Hash )
// This is a workaround for the kernel not discarding block so
// on the thin pool when we remove a thinp device, so we do it
2015-10-06 17:37:21 -04:00
// manually.
2015-12-13 11:00:39 -05:00
// Even if device is deferred deleted, activate it and issue
2015-10-06 17:37:21 -04:00
// discards.
if err := devices . activateDeviceIfNeeded ( info , true ) ; err != nil {
2013-09-04 05:18:23 -04:00
return err
}
2015-10-05 09:02:31 -04:00
devinfo , err := devicemapper . GetInfo ( info . Name ( ) )
if err != nil {
2013-09-04 05:18:23 -04:00
return err
}
2015-10-05 09:02:31 -04:00
if devinfo . OpenCount != 0 {
logrus . Debugf ( "devmapper: Device: %s is in use. OpenCount=%d. Not issuing discards." , info . Hash , devinfo . OpenCount )
return nil
}
if err := devicemapper . BlockDeviceDiscard ( info . DevName ( ) ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error discarding block on device: %s (ignoring)" , err )
2015-10-05 09:02:31 -04:00
}
return nil
}
// Should be called with devices.Lock() held.
2015-10-06 17:37:21 -04:00
func ( devices * DeviceSet ) deleteDevice ( info * devInfo , syncDelete bool ) error {
2015-10-05 09:02:31 -04:00
if devices . doBlkDiscard {
devices . issueDiscard ( info )
}
// Try to deactivate device in case it is active.
if err := devices . deactivateDevice ( info ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Error deactivating device: %s" , err )
2015-10-05 09:02:31 -04:00
return err
}
2015-10-06 17:37:21 -04:00
if err := devices . deleteTransaction ( info , syncDelete ) ; err != nil {
2014-12-03 13:06:43 -05:00
return err
}
2013-09-04 05:18:23 -04:00
return nil
}
2015-10-06 17:37:21 -04:00
// DeleteDevice will return success if device has been marked for deferred
// removal. If one wants to override that and want DeleteDevice() to fail if
// device was busy and could not be deleted, set syncDelete=true.
func ( devices * DeviceSet ) DeleteDevice ( hash string , syncDelete bool ) error {
logrus . Debugf ( "devmapper: DeleteDevice(hash=%v syncDelete=%v) START" , hash , syncDelete )
defer logrus . Debugf ( "devmapper: DeleteDevice(hash=%v syncDelete=%v) END" , hash , syncDelete )
2015-10-01 13:02:55 -04:00
info , err := devices . lookupDeviceWithLock ( hash )
2014-04-01 04:45:40 -04:00
if err != nil {
return err
2014-03-06 12:14:56 -05:00
}
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 05:28:21 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2015-10-06 17:37:21 -04:00
return devices . deleteDevice ( info , syncDelete )
2013-10-07 08:06:24 -04:00
}
2014-02-11 06:47:59 -05:00
func ( devices * DeviceSet ) deactivatePool ( ) error {
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: deactivatePool()" )
defer logrus . Debug ( "devmapper: deactivatePool END" )
2014-02-11 06:47:59 -05:00
devname := devices . getPoolDevName ( )
2014-09-25 10:57:37 -04:00
2014-11-05 18:10:38 -05:00
devinfo , err := devicemapper . GetInfo ( devname )
2014-02-11 06:47:59 -05:00
if err != nil {
return err
}
2015-08-07 10:18:20 -04:00
if devinfo . Exists == 0 {
return nil
2014-09-25 10:57:37 -04:00
}
2015-08-07 10:18:20 -04:00
if err := devicemapper . RemoveDevice ( devname ) ; err != nil {
return err
}
if d , err := devicemapper . GetDeps ( devname ) ; err == nil {
2015-12-14 16:03:33 -05:00
logrus . Warnf ( "devmapper: device %s still has %d active dependents" , devname , d . Count )
2014-02-11 06:47:59 -05:00
}
return nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) deactivateDevice ( info * devInfo ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: deactivateDevice(%s)" , info . Hash )
defer logrus . Debugf ( "devmapper: deactivateDevice END(%s)" , info . Hash )
2014-02-11 06:55:40 -05:00
2014-11-05 18:10:38 -05:00
devinfo , err := devicemapper . GetInfo ( info . Name ( ) )
2013-09-04 05:18:23 -04:00
if err != nil {
return err
}
2015-04-21 18:14:59 -04:00
if devinfo . Exists == 0 {
return nil
}
if devices . deferredRemove {
if err := devicemapper . RemoveDeviceDeferred ( info . Name ( ) ) ; err != nil {
return err
}
} else {
2015-04-02 16:47:14 -04:00
if err := devices . removeDevice ( info . Name ( ) ) ; err != nil {
2013-09-04 05:18:23 -04:00
return err
}
}
return nil
}
2015-04-02 16:47:14 -04:00
// Issues the underlying dm remove operation.
func ( devices * DeviceSet ) removeDevice ( devname string ) error {
2014-02-05 15:37:53 -05:00
var err error
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: removeDevice START(%s)" , devname )
defer logrus . Debugf ( "devmapper: removeDevice END(%s)" , devname )
2015-04-02 16:47:14 -04:00
2015-04-02 16:47:14 -04:00
for i := 0 ; i < 200 ; i ++ {
2014-11-05 18:10:38 -05:00
err = devicemapper . RemoveDevice ( devname )
2014-02-05 15:37:53 -05:00
if err == nil {
break
}
2014-11-05 18:10:38 -05:00
if err != devicemapper . ErrBusy {
2014-02-05 15:37:53 -05:00
return err
}
// If we see EBUSY it may be a transient error,
// sleep a bit a retry a few times.
2014-03-06 12:25:43 -05:00
devices . Unlock ( )
2015-04-02 16:47:14 -04:00
time . Sleep ( 100 * time . Millisecond )
2014-03-06 12:25:43 -05:00
devices . Lock ( )
2014-02-05 15:37:53 -05:00
}
2015-04-02 16:47:14 -04:00
return err
2013-10-16 19:26:37 -04:00
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) cancelDeferredRemoval ( info * devInfo ) error {
2015-04-21 18:14:59 -04:00
if ! devices . deferredRemove {
return nil
}
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: cancelDeferredRemoval START(%s)" , info . Name ( ) )
defer logrus . Debugf ( "devmapper: cancelDeferredRemoval END(%s)" , info . Name ( ) )
2015-04-21 18:14:59 -04:00
devinfo , err := devicemapper . GetInfoWithDeferred ( info . Name ( ) )
if devinfo != nil && devinfo . DeferredRemove == 0 {
return nil
}
// Cancel deferred remove
for i := 0 ; i < 100 ; i ++ {
err = devicemapper . CancelDeferredRemove ( info . Name ( ) )
if err == nil {
break
}
if err == devicemapper . ErrEnxio {
// Device is probably already gone. Return success.
return nil
}
if err != devicemapper . ErrBusy {
return err
}
// If we see EBUSY it may be a transient error,
// sleep a bit a retry a few times.
devices . Unlock ( )
time . Sleep ( 100 * time . Millisecond )
devices . Lock ( )
}
return err
}
2015-07-22 20:42:28 -04:00
// Shutdown shuts down the device by unmounting the root.
2016-03-09 16:23:04 -05:00
func ( devices * DeviceSet ) Shutdown ( home string ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: [deviceset %s] Shutdown()" , devices . devicePrefix )
logrus . Debugf ( "devmapper: Shutting down DeviceSet: %s" , devices . root )
defer logrus . Debugf ( "devmapper: [deviceset %s] Shutdown() END" , devices . devicePrefix )
2013-11-20 15:49:01 -05:00
2015-10-06 17:37:21 -04:00
// Stop deletion worker. This should start delivering new events to
// ticker channel. That means no new instance of cleanupDeletedDevice()
// will run after this call. If one instance is already running at
// the time of the call, it must be holding devices.Lock() and
// we will block on this lock till cleanup function exits.
devices . deletionWorkerTicker . Stop ( )
2015-10-01 13:02:55 -04:00
devices . Lock ( )
2015-10-05 09:02:31 -04:00
// Save DeviceSet Metadata first. Docker kills all threads if they
// don't finish in certain time. It is possible that Shutdown()
// routine does not finish in time as we loop trying to deactivate
// some devices while these are busy. In that case shutdown() routine
// will be killed and we will not get a chance to save deviceset
// metadata. Hence save this early before trying to deactivate devices.
devices . saveDeviceSetMetaData ( )
2016-03-09 16:23:04 -05:00
// ignore the error since it's just a best effort to not try to unmount something that's mounted
mounts , _ := mount . GetMounts ( )
mounted := make ( map [ string ] bool , len ( mounts ) )
for _ , mnt := range mounts {
mounted [ mnt . Mountpoint ] = true
2014-04-01 05:05:30 -04:00
}
2016-03-09 16:23:04 -05:00
if err := filepath . Walk ( path . Join ( home , "mnt" ) , func ( p string , info os . FileInfo , err error ) error {
if err != nil {
return err
}
if ! info . IsDir ( ) {
return nil
}
if mounted [ p ] {
2014-03-20 12:32:59 -04:00
// We use MNT_DETACH here in case it is still busy in some running
// container. This means it'll go away from the global scope directly,
// and the device will be released when that container dies.
2016-03-09 16:23:04 -05:00
if err := syscall . Unmount ( p , syscall . MNT_DETACH ) ; err != nil {
logrus . Debugf ( "devmapper: Shutdown unmounting %s, error: %s" , p , err )
2013-09-09 07:47:29 -04:00
}
2016-03-09 16:23:04 -05:00
}
2014-03-06 12:14:56 -05:00
2016-03-09 16:23:04 -05:00
if devInfo , err := devices . lookupDevice ( path . Base ( p ) ) ; err != nil {
logrus . Debugf ( "devmapper: Shutdown lookup device %s, error: %s" , path . Base ( p ) , err )
} else {
if err := devices . deactivateDevice ( devInfo ) ; err != nil {
logrus . Debugf ( "devmapper: Shutdown deactivate %s , error: %s" , devInfo . Hash , err )
2014-03-20 12:32:59 -04:00
}
2013-09-09 07:47:29 -04:00
}
2016-03-09 16:23:04 -05:00
return nil
} ) ; err != nil && ! os . IsNotExist ( err ) {
devices . Unlock ( )
return err
2013-09-09 07:47:29 -04:00
}
2016-03-09 16:23:04 -05:00
devices . Unlock ( )
2015-10-01 13:02:55 -04:00
info , _ := devices . lookupDeviceWithLock ( "" )
2014-04-01 04:31:34 -04:00
if info != nil {
2014-04-01 05:28:21 -04:00
info . lock . Lock ( )
devices . Lock ( )
2014-04-01 04:31:34 -04:00
if err := devices . deactivateDevice ( info ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Shutdown deactivate base , error: %s" , err )
2014-04-01 04:31:34 -04:00
}
2014-04-01 05:28:21 -04:00
devices . Unlock ( )
info . lock . Unlock ( )
2014-03-28 10:58:14 -04:00
}
2014-04-01 05:28:21 -04:00
devices . Lock ( )
2014-10-24 19:25:24 -04:00
if devices . thinPoolDevice == "" {
if err := devices . deactivatePool ( ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Shutdown deactivate pool , error: %s" , err )
2014-10-24 19:25:24 -04:00
}
2013-09-09 07:47:29 -04:00
}
2014-04-01 05:28:21 -04:00
devices . Unlock ( )
2013-09-09 07:47:29 -04:00
return nil
}
2015-07-22 20:42:28 -04:00
// MountDevice mounts the device if not already mounted.
2014-04-17 19:47:27 -04:00
func ( devices * DeviceSet ) MountDevice ( hash , path , mountLabel string ) error {
2015-10-01 13:02:55 -04:00
info , err := devices . lookupDeviceWithLock ( hash )
2014-04-01 04:45:40 -04:00
if err != nil {
return err
2014-02-06 17:08:17 -05:00
}
2014-02-04 12:19:09 -05:00
2015-10-06 17:37:21 -04:00
if info . Deleted {
return fmt . Errorf ( "devmapper: Can't mount device %v as it has been marked for deferred deletion" , info . Hash )
}
2014-03-06 12:14:56 -05:00
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 05:28:21 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2015-10-06 17:37:21 -04:00
if err := devices . activateDeviceIfNeeded ( info , false ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error activating devmapper device for '%s': %s" , hash , err )
2013-09-04 05:18:23 -04:00
}
2014-03-18 07:26:42 -04:00
fstype , err := ProbeFsType ( info . DevName ( ) )
if err != nil {
return err
}
2014-03-18 09:23:43 -04:00
options := ""
if fstype == "xfs" {
// XFS needs nouuid or it can't mount filesystems with the same fs
options = joinMountOptions ( options , "nouuid" )
}
options = joinMountOptions ( options , devices . mountOptions )
options = joinMountOptions ( options , label . FormatMountLabel ( "" , mountLabel ) )
2015-09-08 15:23:44 -04:00
if err := mount . Mount ( info . DevName ( ) , path , fstype , options ) ; err != nil {
2015-12-14 16:03:33 -05:00
return fmt . Errorf ( "devmapper: Error mounting '%s' on '%s': %s" , info . DevName ( ) , path , err )
2013-09-04 05:18:23 -04:00
}
2013-09-09 07:47:29 -04:00
2014-04-24 17:49:44 -04:00
return nil
2013-09-04 05:18:23 -04:00
}
2015-07-22 20:42:28 -04:00
// UnmountDevice unmounts the device and removes it from hash.
2015-12-03 09:22:25 -05:00
func ( devices * DeviceSet ) UnmountDevice ( hash , mountPath string ) error {
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: UnmountDevice(hash=%s)" , hash )
defer logrus . Debugf ( "devmapper: UnmountDevice(hash=%s) END" , hash )
2013-10-07 08:06:24 -04:00
2015-10-01 13:02:55 -04:00
info , err := devices . lookupDeviceWithLock ( hash )
2014-04-01 04:45:40 -04:00
if err != nil {
return err
2014-02-06 16:26:09 -05:00
}
2014-02-04 12:19:09 -05:00
2014-03-06 12:14:56 -05:00
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 05:28:21 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2015-12-14 16:03:33 -05:00
logrus . Debugf ( "devmapper: Unmount(%s)" , mountPath )
2015-12-03 09:22:25 -05:00
if err := syscall . Unmount ( mountPath , syscall . MNT_DETACH ) ; err != nil {
2013-09-09 06:39:42 -04:00
return err
}
2016-06-11 16:16:55 -04:00
logrus . Debug ( "devmapper: Unmount done" )
2014-03-20 12:32:59 -04:00
2014-04-01 04:31:34 -04:00
if err := devices . deactivateDevice ( info ) ; err != nil {
2013-10-16 19:06:07 -04:00
return err
}
2013-09-09 06:39:42 -04:00
return nil
}
2015-09-30 15:21:22 -04:00
// HasDevice returns true if the device metadata exists.
2013-10-17 20:07:34 -04:00
func ( devices * DeviceSet ) HasDevice ( hash string ) bool {
2015-10-01 13:02:55 -04:00
info , _ := devices . lookupDeviceWithLock ( hash )
2014-04-01 04:45:40 -04:00
return info != nil
2013-09-04 05:18:23 -04:00
}
2015-07-22 20:42:28 -04:00
// List returns a list of device ids.
2013-11-18 05:33:07 -05:00
func ( devices * DeviceSet ) List ( ) [ ] string {
devices . Lock ( )
defer devices . Unlock ( )
ids := make ( [ ] string , len ( devices . Devices ) )
i := 0
for k := range devices . Devices {
ids [ i ] = k
i ++
}
return ids
}
func ( devices * DeviceSet ) deviceStatus ( devName string ) ( sizeInSectors , mappedSectors , highestMappedSector uint64 , err error ) {
var params string
2014-11-05 18:10:38 -05:00
_ , sizeInSectors , _ , params , err = devicemapper . GetStatus ( devName )
2013-11-18 05:33:07 -05:00
if err != nil {
return
}
if _ , err = fmt . Sscanf ( params , "%d %d" , & mappedSectors , & highestMappedSector ) ; err == nil {
return
}
return
}
2015-07-22 20:42:28 -04:00
// GetDeviceStatus provides size, mapped sectors
2013-11-18 05:33:07 -05:00
func ( devices * DeviceSet ) GetDeviceStatus ( hash string ) ( * DevStatus , error ) {
2015-10-01 13:02:55 -04:00
info , err := devices . lookupDeviceWithLock ( hash )
2014-04-01 04:45:40 -04:00
if err != nil {
return nil , err
2013-11-18 05:33:07 -05:00
}
2014-03-06 12:14:56 -05:00
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 05:28:21 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2013-11-18 05:33:07 -05:00
status := & DevStatus {
2015-07-22 20:42:28 -04:00
DeviceID : info . DeviceID ,
2013-11-18 05:33:07 -05:00
Size : info . Size ,
2015-07-22 20:42:28 -04:00
TransactionID : info . TransactionID ,
2013-11-18 05:33:07 -05:00
}
2015-10-06 17:37:21 -04:00
if err := devices . activateDeviceIfNeeded ( info , false ) ; err != nil {
2015-12-14 16:03:33 -05:00
return nil , fmt . Errorf ( "devmapper: Error activating devmapper device for '%s': %s" , hash , err )
2013-11-18 05:33:07 -05:00
}
2015-03-25 04:53:04 -04:00
sizeInSectors , mappedSectors , highestMappedSector , err := devices . deviceStatus ( info . DevName ( ) )
if err != nil {
2013-11-18 05:33:07 -05:00
return nil , err
}
2015-03-25 04:53:04 -04:00
status . SizeInSectors = sizeInSectors
status . MappedSectors = mappedSectors
status . HighestMappedSector = highestMappedSector
2013-11-18 05:33:07 -05:00
return status , nil
}
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) poolStatus ( ) ( totalSizeInSectors , transactionID , dataUsed , dataTotal , metadataUsed , metadataTotal uint64 , err error ) {
2013-11-18 05:33:07 -05:00
var params string
2014-11-05 18:10:38 -05:00
if _ , totalSizeInSectors , _ , params , err = devicemapper . GetStatus ( devices . getPoolName ( ) ) ; err == nil {
2015-07-22 20:42:28 -04:00
_ , err = fmt . Sscanf ( params , "%d %d/%d %d/%d" , & transactionID , & metadataUsed , & metadataTotal , & dataUsed , & dataTotal )
2013-11-18 05:33:07 -05:00
}
return
}
2015-03-01 13:11:15 -05:00
// DataDevicePath returns the path to the data storage for this deviceset,
2015-01-09 11:20:07 -05:00
// regardless of loopback or block device
2015-01-14 17:03:00 -05:00
func ( devices * DeviceSet ) DataDevicePath ( ) string {
2015-01-09 11:20:07 -05:00
return devices . dataDevice
}
// MetadataDevicePath returns the path to the metadata storage for this deviceset,
// regardless of loopback or block device
2015-01-14 17:03:00 -05:00
func ( devices * DeviceSet ) MetadataDevicePath ( ) string {
2015-01-09 11:20:07 -05:00
return devices . metadataDevice
}
2015-01-31 01:21:34 -05:00
func ( devices * DeviceSet ) getUnderlyingAvailableSpace ( loopFile string ) ( uint64 , error ) {
buf := new ( syscall . Statfs_t )
2015-04-26 12:50:25 -04:00
if err := syscall . Statfs ( loopFile , buf ) ; err != nil {
2015-12-14 16:03:33 -05:00
logrus . Warnf ( "devmapper: Couldn't stat loopfile filesystem %v: %v" , loopFile , err )
2015-01-31 01:21:34 -05:00
return 0 , err
}
return buf . Bfree * uint64 ( buf . Bsize ) , nil
}
func ( devices * DeviceSet ) isRealFile ( loopFile string ) ( bool , error ) {
if loopFile != "" {
fi , err := os . Stat ( loopFile )
if err != nil {
2015-12-14 16:03:33 -05:00
logrus . Warnf ( "devmapper: Couldn't stat loopfile %v: %v" , loopFile , err )
2015-01-31 01:21:34 -05:00
return false , err
}
return fi . Mode ( ) . IsRegular ( ) , nil
}
return false , nil
}
2015-01-09 11:20:07 -05:00
// Status returns the current status of this deviceset
2013-10-18 03:17:13 -04:00
func ( devices * DeviceSet ) Status ( ) * Status {
2013-10-17 05:16:50 -04:00
devices . Lock ( )
defer devices . Unlock ( )
2013-11-07 18:58:03 -05:00
status := & Status { }
2013-10-17 05:16:50 -04:00
status . PoolName = devices . getPoolName ( )
2015-01-09 11:20:07 -05:00
status . DataFile = devices . DataDevicePath ( )
status . DataLoopback = devices . dataLoopFile
status . MetadataFile = devices . MetadataDevicePath ( )
status . MetadataLoopback = devices . metadataLoopFile
2015-01-19 16:28:02 -05:00
status . UdevSyncSupported = devicemapper . UdevSyncSupported ( )
2015-04-21 18:14:59 -04:00
status . DeferredRemoveEnabled = devices . deferredRemove
2015-10-06 17:37:21 -04:00
status . DeferredDeleteEnabled = devices . deferredDelete
2015-10-06 17:37:21 -04:00
status . DeferredDeletedDeviceCount = devices . nrDeletedDevices
2015-10-10 10:52:05 -04:00
status . BaseDeviceSize = devices . getBaseDeviceSize ( )
2015-11-13 13:56:21 -05:00
status . BaseDeviceFS = devices . getBaseDeviceFS ( )
2013-10-17 05:16:50 -04:00
2013-11-18 05:33:07 -05:00
totalSizeInSectors , _ , dataUsed , dataTotal , metadataUsed , metadataTotal , err := devices . poolStatus ( )
2013-10-17 05:16:50 -04:00
if err == nil {
2013-11-18 05:33:07 -05:00
// Convert from blocks to bytes
blockSizeInSectors := totalSizeInSectors / dataTotal
2013-10-17 05:16:50 -04:00
2013-11-18 05:33:07 -05:00
status . Data . Used = dataUsed * blockSizeInSectors * 512
status . Data . Total = dataTotal * blockSizeInSectors * 512
2015-01-31 01:21:34 -05:00
status . Data . Available = status . Data . Total - status . Data . Used
2013-10-17 05:16:50 -04:00
2013-11-18 05:33:07 -05:00
// metadata blocks are always 4k
status . Metadata . Used = metadataUsed * 4096
status . Metadata . Total = metadataTotal * 4096
2015-01-31 01:21:34 -05:00
status . Metadata . Available = status . Metadata . Total - status . Metadata . Used
2013-11-18 05:33:07 -05:00
status . SectorSize = blockSizeInSectors * 512
2015-01-31 01:21:34 -05:00
if check , _ := devices . isRealFile ( devices . dataLoopFile ) ; check {
actualSpace , err := devices . getUnderlyingAvailableSpace ( devices . dataLoopFile )
if err == nil && actualSpace < status . Data . Available {
status . Data . Available = actualSpace
}
}
if check , _ := devices . isRealFile ( devices . metadataLoopFile ) ; check {
actualSpace , err := devices . getUnderlyingAvailableSpace ( devices . metadataLoopFile )
if err == nil && actualSpace < status . Metadata . Available {
status . Metadata . Available = actualSpace
}
}
2016-04-21 11:42:23 -04:00
minFreeData := ( dataTotal * uint64 ( devices . minFreeSpacePercent ) ) / 100
status . MinFreeSpace = minFreeData * blockSizeInSectors * 512
2013-10-17 05:16:50 -04:00
}
return status
}
2015-06-15 14:05:10 -04:00
// Status returns the current status of this deviceset
2015-07-22 20:42:28 -04:00
func ( devices * DeviceSet ) exportDeviceMetadata ( hash string ) ( * deviceMetadata , error ) {
2015-10-01 13:02:55 -04:00
info , err := devices . lookupDeviceWithLock ( hash )
2015-06-15 14:05:10 -04:00
if err != nil {
return nil , err
}
info . lock . Lock ( )
defer info . lock . Unlock ( )
2015-07-22 20:42:28 -04:00
metadata := & deviceMetadata { info . DeviceID , info . Size , info . Name ( ) }
2015-06-15 14:05:10 -04:00
return metadata , nil
}
2015-07-22 20:42:28 -04:00
// NewDeviceSet creates the device set based on the options provided.
2015-10-08 11:51:41 -04:00
func NewDeviceSet ( root string , doInit bool , options [ ] string , uidMaps , gidMaps [ ] idtools . IDMap ) ( * DeviceSet , error ) {
2014-11-05 18:10:38 -05:00
devicemapper . SetDevDir ( "/dev" )
2013-09-05 09:32:57 -04:00
2013-10-24 15:04:49 -04:00
devices := & DeviceSet {
2015-07-07 15:27:19 -04:00
root : root ,
2015-07-22 20:42:28 -04:00
metaData : metaData { Devices : make ( map [ string ] * devInfo ) } ,
dataLoopbackSize : defaultDataLoopbackSize ,
metaDataLoopbackSize : defaultMetaDataLoopbackSize ,
baseFsSize : defaultBaseFsSize ,
overrideUdevSyncCheck : defaultUdevSyncOverride ,
2015-07-07 15:27:19 -04:00
doBlkDiscard : true ,
2015-07-22 20:42:28 -04:00
thinpBlockSize : defaultThinpBlockSize ,
deviceIDMap : make ( [ ] byte , deviceIDMapSz ) ,
2015-10-06 17:37:21 -04:00
deletionWorkerTicker : time . NewTicker ( time . Second * 30 ) ,
2015-10-08 11:51:41 -04:00
uidMaps : uidMaps ,
gidMaps : gidMaps ,
2016-03-07 15:27:39 -05:00
minFreeSpacePercent : defaultMinFreeSpacePercent ,
2014-03-27 12:48:32 -04:00
}
2014-04-01 09:41:25 -04:00
foundBlkDiscard := false
2014-03-27 12:48:32 -04:00
for _ , option := range options {
2014-07-28 20:23:38 -04:00
key , val , err := parsers . ParseKeyValueOpt ( option )
2014-03-27 12:48:32 -04:00
if err != nil {
return nil , err
}
key = strings . ToLower ( key )
switch key {
case "dm.basesize" :
2014-06-23 15:36:08 -04:00
size , err := units . RAMInBytes ( val )
2014-03-27 12:48:32 -04:00
if err != nil {
return nil , err
}
2016-01-12 13:44:13 -05:00
userBaseSize = true
2014-03-27 12:48:32 -04:00
devices . baseFsSize = uint64 ( size )
case "dm.loopdatasize" :
2014-06-23 15:36:08 -04:00
size , err := units . RAMInBytes ( val )
2014-03-27 12:48:32 -04:00
if err != nil {
return nil , err
}
devices . dataLoopbackSize = size
case "dm.loopmetadatasize" :
2014-06-23 15:36:08 -04:00
size , err := units . RAMInBytes ( val )
2014-03-27 12:48:32 -04:00
if err != nil {
return nil , err
}
devices . metaDataLoopbackSize = size
2014-03-18 09:23:43 -04:00
case "dm.fs" :
if val != "ext4" && val != "xfs" {
2015-12-14 16:03:33 -05:00
return nil , fmt . Errorf ( "devmapper: Unsupported filesystem %s\n" , val )
2014-03-18 09:23:43 -04:00
}
2015-11-13 13:56:21 -05:00
devices . filesystem = val
2014-03-18 09:23:43 -04:00
case "dm.mkfsarg" :
devices . mkfsArgs = append ( devices . mkfsArgs , val )
case "dm.mountopt" :
devices . mountOptions = joinMountOptions ( devices . mountOptions , val )
2014-03-28 10:36:55 -04:00
case "dm.metadatadev" :
devices . metadataDevice = val
case "dm.datadev" :
devices . dataDevice = val
2014-10-24 19:25:24 -04:00
case "dm.thinpooldev" :
devices . thinPoolDevice = strings . TrimPrefix ( val , "/dev/mapper/" )
2014-04-01 09:41:25 -04:00
case "dm.blkdiscard" :
foundBlkDiscard = true
devices . doBlkDiscard , err = strconv . ParseBool ( val )
if err != nil {
return nil , err
}
2014-06-20 11:53:18 -04:00
case "dm.blocksize" :
size , err := units . RAMInBytes ( val )
if err != nil {
return nil , err
}
// convert to 512b sectors
devices . thinpBlockSize = uint32 ( size ) >> 9
2015-07-07 15:27:19 -04:00
case "dm.override_udev_sync_check" :
devices . overrideUdevSyncCheck , err = strconv . ParseBool ( val )
if err != nil {
return nil , err
}
2015-04-21 18:14:59 -04:00
case "dm.use_deferred_removal" :
2015-07-22 20:42:28 -04:00
enableDeferredRemoval , err = strconv . ParseBool ( val )
2015-04-21 18:14:59 -04:00
if err != nil {
return nil , err
}
2015-10-06 17:37:21 -04:00
case "dm.use_deferred_deletion" :
enableDeferredDeletion , err = strconv . ParseBool ( val )
if err != nil {
return nil , err
}
2016-03-07 15:27:39 -05:00
case "dm.min_free_space" :
if ! strings . HasSuffix ( val , "%" ) {
return nil , fmt . Errorf ( "devmapper: Option dm.min_free_space requires %% suffix" )
}
valstring := strings . TrimSuffix ( val , "%" )
minFreeSpacePercent , err := strconv . ParseUint ( valstring , 10 , 32 )
if err != nil {
return nil , err
}
if minFreeSpacePercent >= 100 {
return nil , fmt . Errorf ( "devmapper: Invalid value %v for option dm.min_free_space" , val )
}
devices . minFreeSpacePercent = uint32 ( minFreeSpacePercent )
2014-03-27 12:48:32 -04:00
default :
2015-12-14 16:03:33 -05:00
return nil , fmt . Errorf ( "devmapper: Unknown option %s\n" , key )
2014-03-27 12:48:32 -04:00
}
2013-09-04 05:18:23 -04:00
}
2013-10-24 15:04:49 -04:00
2014-04-01 09:41:25 -04:00
// By default, don't do blk discard hack on raw devices, its rarely useful and is expensive
2014-11-13 13:37:47 -05:00
if ! foundBlkDiscard && ( devices . dataDevice != "" || devices . thinPoolDevice != "" ) {
2014-04-01 09:41:25 -04:00
devices . doBlkDiscard = false
}
2013-11-18 05:33:07 -05:00
if err := devices . initDevmapper ( doInit ) ; err != nil {
2013-10-24 15:04:49 -04:00
return nil , err
}
return devices , nil
2013-09-04 05:18:23 -04:00
}