2014-08-02 01:46:15 -06:00
// +build linux
2013-11-27 19:12:51 -08:00
2013-09-04 11:18:23 +02:00
package devmapper
import (
"encoding/json"
2013-11-30 01:02:09 -02:00
"errors"
2013-09-04 11:18:23 +02:00
"fmt"
"io"
"io/ioutil"
2014-05-16 14:10:02 +02:00
"os"
"os/exec"
2013-09-04 11:18:23 +02:00
"path"
"path/filepath"
2013-10-03 21:00:16 +02:00
"strconv"
2014-03-27 17:48:32 +01:00
"strings"
2013-10-07 14:06:24 +02:00
"sync"
2014-03-20 17:32:59 +01:00
"syscall"
2013-10-17 15:04:14 -07:00
"time"
2014-04-28 14:36:04 -07:00
2015-03-26 23:22:04 +01:00
"github.com/Sirupsen/logrus"
2014-10-24 15:11:48 -07:00
"github.com/docker/docker/daemon/graphdriver"
2014-11-05 18:10:38 -05:00
"github.com/docker/docker/pkg/devicemapper"
2014-07-28 17:23:38 -07:00
"github.com/docker/docker/pkg/parsers"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/pkg/units"
2015-07-16 16:00:55 -07:00
"github.com/opencontainers/runc/libcontainer/label"
2013-09-04 11:18:23 +02:00
)
2013-10-11 20:37:11 -07:00
var (
2015-07-23 00:42:28 +00:00
defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024
defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024
defaultBaseFsSize uint64 = 100 * 1024 * 1024 * 1024
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.
2015-07-23 00:42:28 +00:00
logLevel = devicemapper . LogLevelFatal
driverDeferredRemovalSupport = false
enableDeferredRemoval = false
2013-10-02 20:18:15 -07:00
)
2013-09-04 11:18:23 +02: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-23 00:42:28 +00: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-23 00:42:28 +00:00
type devInfo struct {
2014-12-12 11:01:46 -08:00
Hash string ` json:"-" `
2015-07-23 00:42:28 +00:00
DeviceID int ` json:"device_id" `
2014-12-12 11:01:46 -08:00
Size uint64 ` json:"size" `
2015-07-23 00:42:28 +00:00
TransactionID uint64 ` json:"transaction_id" `
2014-12-12 11:01:46 -08:00
Initialized bool ` json:"initialized" `
devices * DeviceSet
2014-02-04 18:19:09 +01:00
2014-12-12 11:01:46 -08:00
mountCount int
mountPath string
2014-03-06 18:14:56 +01: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 11:28:21 +02:00
//
// WARNING: In order to avoid AB-BA deadlocks when releasing
// the global lock while holding the per-device locks all
// device locks must be aquired *before* the device lock, and
// multiple device locks should be aquired parent before child.
2014-12-12 11:01:46 -08:00
lock sync . Mutex
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
type metaData struct {
Devices map [ string ] * devInfo ` json:"Devices" `
2014-12-12 11:01:46 -08:00
devicesLock sync . Mutex // Protects all read/writes to Devices map
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
// DeviceSet holds information about list of devices
2013-10-18 00:07:34 +00:00
type DeviceSet struct {
2015-07-23 00:42:28 +00:00
metaData ` json:"-" `
2014-12-03 13:06:43 -05:00
sync . Mutex ` json:"-" ` // Protects Devices map and serializes calls into libdevmapper
root string
devicePrefix string
2015-07-23 00:42:28 +00:00
TransactionID uint64 ` json:"-" `
NextDeviceID int ` json:"next_device_id" `
deviceIDMap [ ] byte
2014-03-27 17:48:32 +01:00
// Options
2015-07-07 12:27:19 -07:00
dataLoopbackSize int64
metaDataLoopbackSize int64
baseFsSize uint64
filesystem string
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-23 00:42:28 +00:00
transaction ` json:"-" `
2015-07-07 12:27:19 -07:00
overrideUdevSyncCheck bool
deferredRemove bool // use deferred removal
BaseDeviceUUID string //save UUID of base device
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
// DiskUsage contains information about disk usage and is used when reporting Status of a device.
2013-10-17 11:16:50 +02:00
type DiskUsage struct {
2015-07-23 00:42:28 +00:00
// Used bytes on the disk.
Used uint64
// Total bytes on the disk.
Total uint64
// Available bytes on the disk.
2015-01-30 22:21:34 -08:00
Available uint64
2013-10-17 11:16:50 +02:00
}
2015-07-23 00:42:28 +00:00
// Status returns the information about the device.
2013-10-17 11:16:50 +02:00
type Status struct {
2015-07-23 00:42:28 +00: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
// 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
2013-11-18 11:33:07 +01:00
}
2015-06-15 14:05:10 -04:00
// Structure used to export image/container metadata in docker inspect.
2015-07-23 00:42:28 +00: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-23 00:42:28 +00:00
// DevStatus returns information about device mounted containing its id, size and sector information.
2013-11-18 11:33:07 +01:00
type DevStatus struct {
2015-07-23 00:42:28 +00: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 11:33:07 +01:00
HighestMappedSector uint64
2013-10-17 11:16:50 +02:00
}
2013-09-04 11:18:23 +02:00
func getDevName ( name string ) string {
return "/dev/mapper/" + name
}
2015-07-23 00:42:28 +00:00
func ( info * devInfo ) Name ( ) string {
2013-09-04 11:18:23 +02:00
hash := info . Hash
if hash == "" {
hash = "base"
}
2013-09-05 15:32:57 +02:00
return fmt . Sprintf ( "%s-%s" , info . devices . devicePrefix , hash )
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
func ( info * devInfo ) DevName ( ) string {
2013-09-04 11:18:23 +02:00
return getDevName ( info . Name ( ) )
}
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) loopbackDir ( ) string {
2013-10-15 11:30:06 -07:00
return path . Join ( devices . root , "devicemapper" )
2013-09-04 11:18:23 +02:00
}
2014-04-24 23:49:44 +02:00
func ( devices * DeviceSet ) metadataDir ( ) string {
return path . Join ( devices . root , "metadata" )
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) metadataFile ( info * devInfo ) string {
2014-04-24 23:49:44 +02: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 23:49:44 +02:00
func ( devices * DeviceSet ) oldMetadataFile ( ) string {
2013-09-04 11:18:23 +02:00
return path . Join ( devices . loopbackDir ( ) , "json" )
}
2013-10-18 00:07:34 +00: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 11:18:23 +02:00
}
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) getPoolDevName ( ) string {
2013-09-04 11:18:23 +02:00
return getDevName ( devices . getPoolName ( ) )
}
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) hasImage ( name string ) bool {
2013-09-04 11:18:23 +02:00
dirname := devices . loopbackDir ( )
filename := path . Join ( dirname , name )
2014-05-16 14:10:02 +02:00
_ , err := os . Stat ( filename )
2013-09-04 11:18:23 +02:00
return err == nil
}
2013-10-17 01:42:05 +00:00
// ensureImage creates a sparse file of <size> bytes at the path
// <root>/devicemapper/<name>.
// If the file already exists, it does nothing.
// Either way it returns the full path.
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) ensureImage ( name string , size int64 ) ( string , error ) {
2013-09-04 11:18:23 +02:00
dirname := devices . loopbackDir ( )
filename := path . Join ( dirname , name )
Simplify and fix os.MkdirAll() usage
TL;DR: check for IsExist(err) after a failed MkdirAll() is both
redundant and wrong -- so two reasons to remove it.
Quoting MkdirAll documentation:
> MkdirAll creates a directory named path, along with any necessary
> parents, and returns nil, or else returns an error. If path
> is already a directory, MkdirAll does nothing and returns nil.
This means two things:
1. If a directory to be created already exists, no error is returned.
2. If the error returned is IsExist (EEXIST), it means there exists
a non-directory with the same name as MkdirAll need to use for
directory. Example: we want to MkdirAll("a/b"), but file "a"
(or "a/b") already exists, so MkdirAll fails.
The above is a theory, based on quoted documentation and my UNIX
knowledge.
3. In practice, though, current MkdirAll implementation [1] returns
ENOTDIR in most of cases described in #2, with the exception when
there is a race between MkdirAll and someone else creating the
last component of MkdirAll argument as a file. In this very case
MkdirAll() will indeed return EEXIST.
Because of #1, IsExist check after MkdirAll is not needed.
Because of #2 and #3, ignoring IsExist error is just plain wrong,
as directory we require is not created. It's cleaner to report
the error now.
Note this error is all over the tree, I guess due to copy-paste,
or trying to follow the same usage pattern as for Mkdir(),
or some not quite correct examples on the Internet.
[v2: a separate aufs commit is merged into this one]
[1] https://github.com/golang/go/blob/f9ed2f75/src/os/path.go
Signed-off-by: Kir Kolyshkin <kir@openvz.org>
2015-07-29 16:49:05 -07:00
if err := os . MkdirAll ( dirname , 0700 ) ; err != nil {
2013-09-04 11:18:23 +02:00
return "" , err
}
2014-05-16 14:10:02 +02:00
if _ , err := os . Stat ( filename ) ; err != nil {
if ! os . IsNotExist ( err ) {
2013-09-04 11:18:23 +02:00
return "" , err
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Creating loopback file %s for device-manage use" , filename )
2014-05-16 14:10:02 +02:00
file , err := os . OpenFile ( filename , os . O_RDWR | os . O_CREATE , 0600 )
2013-09-04 11:18:23 +02:00
if err != nil {
return "" , err
}
2013-11-13 17:05:32 -08:00
defer file . Close ( )
2013-10-02 20:18:15 -07:00
2015-04-26 18:50:25 +02:00
if err := file . Truncate ( size ) ; err != nil {
2013-09-04 11:18:23 +02:00
return "" , err
}
}
return filename , nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) allocateTransactionID ( ) uint64 {
devices . OpenTransactionID = devices . TransactionID + 1
return devices . OpenTransactionID
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) updatePoolTransactionID ( ) error {
if err := devicemapper . SetTransactionId ( devices . getPoolDevName ( ) , devices . TransactionID , devices . OpenTransactionID ) ; err != nil {
2014-12-03 13:06:43 -05:00
return fmt . Errorf ( "Error setting devmapper transaction ID: %s" , err )
2014-12-03 13:06:43 -05:00
}
2015-07-23 00:42:28 +00:00
devices . TransactionID = devices . OpenTransactionID
2014-12-03 13:06:43 -05:00
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) removeMetadata ( info * devInfo ) error {
2014-05-16 14:10:02 +02:00
if err := os . RemoveAll ( devices . metadataFile ( info ) ) ; err != nil {
2014-04-24 23:49:44 +02:00
return fmt . Errorf ( "Error removing metadata file %s: %s" , devices . metadataFile ( info ) , err )
}
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 23:49:44 +02:00
tmpFile , err := ioutil . TempFile ( devices . metadataDir ( ) , ".tmp" )
2013-09-04 11:18:23 +02:00
if err != nil {
2013-10-18 07:09:13 +00:00
return fmt . Errorf ( "Error creating metadata file: %s" , err )
2013-09-04 11:18:23 +02:00
}
n , err := tmpFile . Write ( jsonData )
if err != nil {
2013-10-18 07:09:13 +00:00
return fmt . Errorf ( "Error writing metadata to %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 11:18:23 +02:00
}
if n < len ( jsonData ) {
2013-10-02 20:18:15 -07:00
return io . ErrShortWrite
2013-09-04 11:18:23 +02:00
}
2013-10-02 20:18:15 -07:00
if err := tmpFile . Sync ( ) ; err != nil {
2013-10-18 07:09:13 +00:00
return fmt . Errorf ( "Error syncing metadata file %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 11:18:23 +02:00
}
2013-10-02 20:18:15 -07:00
if err := tmpFile . Close ( ) ; err != nil {
2013-10-18 07:09:13 +00:00
return fmt . Errorf ( "Error closing metadata file %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 11:18:23 +02:00
}
2014-11-05 09:25:02 -05:00
if err := os . Rename ( tmpFile . Name ( ) , filePath ) ; err != nil {
2013-11-29 22:53:20 -02:00
return fmt . Errorf ( "Error committing metadata file %s: %s" , tmpFile . Name ( ) , err )
2013-09-04 11:18:23 +02:00
}
2014-11-05 09:25:02 -05:00
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) saveMetadata ( info * devInfo ) error {
2014-11-05 09:25:02 -05:00
jsonData , err := json . Marshal ( info )
if err != nil {
return fmt . Errorf ( "Error encoding metadata to json: %s" , err )
}
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 11:18:23 +02:00
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) markDeviceIDUsed ( deviceID int ) {
2014-12-03 13:06:43 -05:00
var mask byte
2015-07-23 00:42:28 +00:00
i := deviceID % 8
2014-12-03 13:06:43 -05:00
mask = 1 << uint ( i )
2015-07-23 00:42:28 +00:00
devices . deviceIDMap [ deviceID / 8 ] = devices . deviceIDMap [ deviceID / 8 ] | mask
2014-12-03 13:06:43 -05:00
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) markDeviceIDFree ( deviceID int ) {
2014-12-03 13:06:43 -05:00
var mask byte
2015-07-23 00:42:28 +00:00
i := deviceID % 8
2014-12-03 13:06:43 -05:00
mask = ^ ( 1 << uint ( i ) )
2015-07-23 00:42:28 +00:00
devices . deviceIDMap [ deviceID / 8 ] = devices . deviceIDMap [ deviceID / 8 ] & mask
2014-12-03 13:06:43 -05:00
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) isDeviceIDFree ( deviceID int ) bool {
2014-12-03 13:06:43 -05:00
var mask byte
2015-07-23 00:42:28 +00:00
i := deviceID % 8
2014-12-03 13:06:43 -05:00
mask = ( 1 << uint ( i ) )
2015-07-23 00:42:28 +00:00
if ( devices . deviceIDMap [ deviceID / 8 ] & mask ) != 0 {
2014-12-03 13:06:43 -05:00
return false
}
return true
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) lookupDevice ( hash string ) ( * devInfo , error ) {
2014-04-01 11:05:30 +02:00
devices . devicesLock . Lock ( )
defer devices . devicesLock . Unlock ( )
2014-04-01 10:45:40 +02:00
info := devices . Devices [ hash ]
if info == nil {
2014-04-24 23:49:44 +02:00
info = devices . loadMetadata ( hash )
if info == nil {
return nil , fmt . Errorf ( "Unknown device %s" , hash )
}
devices . Devices [ hash ] = info
2014-04-01 10:45:40 +02:00
}
return info , nil
}
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-03-26 23:22:04 +01:00
logrus . Debugf ( "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-03-26 23:22:04 +01:00
logrus . Debugf ( "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-03-26 23:22:04 +01:00
logrus . Debugf ( "Skipping file %s" , path )
2014-12-03 13:06:43 -05:00
return nil
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Loading data for file %s" , path )
2014-12-03 13:06:43 -05:00
hash := finfo . Name ( )
if hash == "base" {
hash = ""
}
dinfo := devices . loadMetadata ( hash )
if dinfo == nil {
return fmt . Errorf ( "Error loading device metadata file %s" , hash )
}
2015-07-23 00:42:28 +00:00
if dinfo . DeviceID > maxDeviceID {
logrus . Errorf ( "Ignoring Invalid DeviceID=%d" , dinfo . DeviceID )
2014-12-03 13:06:43 -05:00
return nil
}
devices . Lock ( )
2015-07-23 00:42:28 +00:00
devices . markDeviceIDUsed ( dinfo . DeviceID )
2014-12-03 13:06:43 -05:00
devices . Unlock ( )
2015-07-23 00:42:28 +00:00
logrus . Debugf ( "Added deviceID=%d to DeviceIDMap" , dinfo . DeviceID )
2014-12-03 13:06:43 -05:00
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) constructDeviceIDMap ( ) error {
logrus . Debugf ( "[deviceset] constructDeviceIDMap()" )
defer logrus . Debugf ( "[deviceset] constructDeviceIDMap() END" )
2014-12-03 13:06:43 -05:00
var scan = func ( path string , info os . FileInfo , err error ) error {
if err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "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 )
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) unregisterDevice ( id int , hash string ) error {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "unregisterDevice(%v, %v)" , id , hash )
2015-07-23 00:42:28 +00:00
info := & devInfo {
2014-12-03 13:06:43 -05:00
Hash : hash ,
2015-07-23 00:42:28 +00:00
DeviceID : id ,
2014-12-03 13:06:43 -05:00
}
devices . devicesLock . Lock ( )
delete ( devices . Devices , hash )
devices . devicesLock . Unlock ( )
if err := devices . removeMetadata ( info ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error removing metadata: %s" , err )
2014-12-03 13:06:43 -05:00
return err
}
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) registerDevice ( id int , hash string , size uint64 , transactionID uint64 ) ( * devInfo , error ) {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "registerDevice(%v, %v)" , id , hash )
2015-07-23 00:42:28 +00:00
info := & devInfo {
2013-09-04 11:18:23 +02:00
Hash : hash ,
2015-07-23 00:42:28 +00:00
DeviceID : id ,
2013-09-04 11:18:23 +02:00
Size : size ,
2015-07-23 00:42:28 +00:00
TransactionID : transactionID ,
2013-09-04 11:18:23 +02:00
Initialized : false ,
2013-09-05 15:32:57 +02:00
devices : devices ,
2013-09-04 11:18:23 +02:00
}
2014-04-01 11:05:30 +02:00
devices . devicesLock . Lock ( )
2013-09-04 11:18:23 +02:00
devices . Devices [ hash ] = info
2014-04-01 11:05:30 +02:00
devices . devicesLock . Unlock ( )
2014-04-24 23:49:44 +02:00
if err := devices . saveMetadata ( info ) ; err != nil {
2013-09-04 11:18:23 +02:00
// Try to remove unused device
2014-04-01 11:05:30 +02:00
devices . devicesLock . Lock ( )
2013-10-02 20:18:15 -07:00
delete ( devices . Devices , hash )
2014-04-01 11:05:30 +02:00
devices . devicesLock . Unlock ( )
2013-09-04 11:18:23 +02:00
return nil , err
}
return info , nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) activateDeviceIfNeeded ( info * devInfo ) error {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "activateDeviceIfNeeded(%v)" , info . Hash )
2013-09-04 11:18:23 +02:00
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 {
return fmt . Errorf ( "Deivce Deferred Removal Cancellation Failed: %s" , err )
}
2014-11-05 18:10:38 -05:00
if devinfo , _ := devicemapper . GetInfo ( info . Name ( ) ) ; devinfo != nil && devinfo . Exists != 0 {
2013-09-04 11:18:23 +02:00
return nil
}
2015-07-23 00:42:28 +00:00
return devicemapper . ActivateDevice ( devices . getPoolDevName ( ) , info . Name ( ) , info . DeviceID , info . Size )
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) createFilesystem ( info * devInfo ) error {
2013-09-04 11:18:23 +02:00
devname := info . DevName ( )
2014-03-18 14:23:43 +01:00
args := [ ] string { }
for _ , arg := range devices . mkfsArgs {
args = append ( args , arg )
}
args = append ( args , devname )
var err error
switch devices . filesystem {
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 19:32:39 -07:00
if err != nil {
return err
}
err = exec . Command ( "tune2fs" , append ( [ ] string { "-c" , "-1" , "-i" , "0" } , devname ) ... ) . Run ( )
2014-03-18 14:23:43 +01:00
default :
err = fmt . Errorf ( "Unsupported filesystem type %s" , devices . filesystem )
2013-09-04 11:18:23 +02:00
}
if err != nil {
return err
}
2014-03-18 14:23:43 +01:00
2013-09-04 11:18:23 +02:00
return nil
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) migrateOldMetaData ( ) error {
2015-03-02 02:11:15 +08:00
// Migrate old metadata file
2014-04-24 23:49:44 +02:00
jsonData , err := ioutil . ReadFile ( devices . oldMetadataFile ( ) )
2014-05-16 14:10:02 +02:00
if err != nil && ! os . IsNotExist ( err ) {
2013-09-04 11:18:23 +02:00
return err
}
if jsonData != nil {
2015-07-23 00:42:28 +00:00
m := metaData { Devices : make ( map [ string ] * devInfo ) }
2014-04-24 23:49:44 +02:00
if err := json . Unmarshal ( jsonData , & m ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
2014-04-24 23:49:44 +02:00
for hash , info := range m . Devices {
info . Hash = hash
2014-12-03 13:06:43 -05:00
devices . saveMetadata ( info )
2013-09-04 11:18:23 +02:00
}
2014-05-16 14:10:02 +02:00
if err := os . Rename ( devices . oldMetadataFile ( ) , devices . oldMetadataFile ( ) + ".migrated" ) ; err != nil {
2014-04-24 23:49:44 +02:00
return err
2013-09-04 11:18:23 +02:00
}
2014-04-24 23:49:44 +02:00
2013-09-04 11:18:23 +02:00
}
2014-04-24 23:49:44 +02:00
2013-09-04 11:18:23 +02:00
return nil
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) initMetaData ( ) error {
if err := devices . migrateOldMetaData ( ) ; err != nil {
return err
}
2015-07-23 00:42:28 +00:00
_ , transactionID , _ , _ , _ , _ , err := devices . poolStatus ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return err
}
2015-07-23 00:42:28 +00:00
devices . TransactionID = transactionID
2014-12-03 13:06:43 -05:00
2015-07-23 00:42:28 +00:00
if err := devices . constructDeviceIDMap ( ) ; err != nil {
2014-12-03 13:06:43 -05:00
return err
}
2014-12-03 13:06:43 -05:00
if err := devices . processPendingTransaction ( ) ; err != nil {
return err
}
2014-12-03 13:06:43 -05:00
return nil
}
2015-07-23 00:42:28 +00: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-23 00:42:28 +00: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-23 00:42:28 +00:00
devices . incNextDeviceID ( )
2014-12-03 13:06:43 -05:00
}
2015-07-23 00:42:28 +00:00
return 0 , fmt . Errorf ( "Unable to find a free device ID" )
2014-12-03 13:06:43 -05:00
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) createRegisterDevice ( hash string ) ( * devInfo , error ) {
deviceID , err := devices . getNextFreeDeviceID ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return nil , err
}
2015-07-23 00:42:28 +00:00
if err := devices . openTransaction ( hash , deviceID ) ; err != nil {
logrus . Debugf ( "Error opening transaction hash = %s deviceID = %d" , hash , deviceID )
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-23 00:42:28 +00:00
if err := devicemapper . CreateDevice ( devices . getPoolDevName ( ) , deviceID ) ; err != nil {
2014-12-03 13:06:43 -05:00
if devicemapper . DeviceIdExists ( err ) {
2015-07-23 00:42:28 +00:00
// Device ID already exists. This should not
2014-12-03 13:06:43 -05:00
// happen. Now we have a mechianism to find
2015-07-23 00:42:28 +00:00
// a free device ID. So something is not right.
2014-12-03 13:06:43 -05:00
// Give a warning and continue.
2015-07-23 00:42:28 +00:00
logrus . Errorf ( "Device ID %d exists in pool but it is supposed to be unused" , deviceID )
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-23 00:42:28 +00:00
devices . refreshTransaction ( deviceID )
2014-12-03 13:06:43 -05:00
continue
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error creating device: %s" , err )
2015-07-23 00:42:28 +00: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-07-23 00:42:28 +00:00
logrus . Debugf ( "Registering device (id %v) with FS size %v" , deviceID , devices . baseFsSize )
info , err := devices . registerDevice ( deviceID , hash , devices . baseFsSize , devices . OpenTransactionID )
2014-12-03 13:06:43 -05:00
if err != nil {
2015-07-23 00:42:28 +00: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-23 00:42:28 +00: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
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) createRegisterSnapDevice ( hash string , baseInfo * devInfo ) error {
deviceID , err := devices . getNextFreeDeviceID ( )
2014-12-03 13:06:43 -05:00
if err != nil {
return err
}
2015-07-23 00:42:28 +00:00
if err := devices . openTransaction ( hash , deviceID ) ; err != nil {
logrus . Debugf ( "Error opening transaction hash = %s deviceID = %d" , hash , deviceID )
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return err
}
2014-12-03 13:06:43 -05:00
for {
2015-07-23 00:42:28 +00:00
if err := devicemapper . CreateSnapDevice ( devices . getPoolDevName ( ) , deviceID , baseInfo . Name ( ) , baseInfo . DeviceID ) ; err != nil {
2014-12-03 13:06:43 -05:00
if devicemapper . DeviceIdExists ( err ) {
2015-07-23 00:42:28 +00:00
// Device ID already exists. This should not
2014-12-03 13:06:43 -05:00
// happen. Now we have a mechianism to find
2015-07-23 00:42:28 +00:00
// a free device ID. So something is not right.
2014-12-03 13:06:43 -05:00
// Give a warning and continue.
2015-07-23 00:42:28 +00:00
logrus . Errorf ( "Device ID %d exists in pool but it is supposed to be unused" , deviceID )
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-23 00:42:28 +00:00
devices . refreshTransaction ( deviceID )
2014-12-03 13:06:43 -05:00
continue
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error creating snap device: %s" , err )
2015-07-23 00:42:28 +00:00
devices . markDeviceIDFree ( deviceID )
2014-12-03 13:06:43 -05:00
return err
}
break
}
2014-12-03 13:06:43 -05:00
2015-07-23 00:42:28 +00:00
if _ , err := devices . registerDevice ( deviceID , hash , baseInfo . Size , devices . OpenTransactionID ) ; err != nil {
devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceID )
devices . markDeviceIDFree ( deviceID )
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "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-23 00:42:28 +00: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-23 00:42:28 +00:00
func ( devices * DeviceSet ) loadMetadata ( hash string ) * devInfo {
info := & devInfo { Hash : hash , devices : devices }
2014-04-24 23:49:44 +02:00
jsonData , err := ioutil . ReadFile ( devices . metadataFile ( info ) )
if err != nil {
return nil
}
if err := json . Unmarshal ( jsonData , & info ) ; err != nil {
return nil
}
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 {
logrus . Debugf ( "Failed to find uuid for device %s:%v" , device , err )
return "" , err
}
uuid := strings . TrimSuffix ( string ( out ) , "\n" )
uuid = strings . TrimSpace ( uuid )
logrus . Debugf ( "UUID for device: %s is:%s" , device , uuid )
return uuid , nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) verifyBaseDeviceUUID ( baseInfo * devInfo ) error {
2015-07-08 09:05:37 -07:00
devices . Lock ( )
defer devices . Unlock ( )
2015-06-16 21:12:27 -04:00
if err := devices . activateDeviceIfNeeded ( baseInfo ) ; err != nil {
return err
}
defer devices . deactivateDevice ( baseInfo )
uuid , err := getDeviceUUID ( baseInfo . DevName ( ) )
if err != nil {
return err
}
if devices . BaseDeviceUUID != uuid {
return fmt . Errorf ( "Current Base Device UUID:%s does not match with stored UUID:%s" , uuid , devices . BaseDeviceUUID )
}
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) saveBaseDeviceUUID ( baseInfo * devInfo ) error {
2015-07-08 09:05:37 -07:00
devices . Lock ( )
defer devices . Unlock ( )
2015-06-16 21:12:27 -04:00
if err := devices . activateDeviceIfNeeded ( baseInfo ) ; err != nil {
return err
}
defer devices . deactivateDevice ( baseInfo )
uuid , err := getDeviceUUID ( baseInfo . DevName ( ) )
if err != nil {
return err
}
devices . BaseDeviceUUID = uuid
devices . saveDeviceSetMetaData ( )
return nil
}
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) setupBaseImage ( ) error {
2014-04-01 10:45:40 +02:00
oldInfo , _ := devices . lookupDevice ( "" )
2013-09-04 11:18:23 +02:00
if oldInfo != nil && oldInfo . Initialized {
2015-06-16 21:12:27 -04:00
// If BaseDeviceUUID is nil (upgrade case), save it and
// return success.
if devices . BaseDeviceUUID == "" {
if err := devices . saveBaseDeviceUUID ( oldInfo ) ; err != nil {
return fmt . Errorf ( "Could not query and save base device UUID:%v" , err )
}
return nil
}
if err := devices . verifyBaseDeviceUUID ( oldInfo ) ; err != nil {
return fmt . Errorf ( "Base Device UUID verification failed. Possibly using a different thin pool then last invocation:%v" , err )
}
2013-09-04 11:18:23 +02:00
return nil
}
if oldInfo != nil && ! oldInfo . Initialized {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Removing uninitialized base image" )
2014-12-03 13:06:43 -05:00
if err := devices . DeleteDevice ( "" ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
}
2014-10-24 19:25:24 -04:00
if devices . thinPoolDevice != "" && oldInfo == nil {
2015-07-23 00:42:28 +00:00
_ , transactionID , dataUsed , _ , _ , _ , err := devices . poolStatus ( )
2014-11-21 22:26:09 -05:00
if err != nil {
2014-10-24 19:25:24 -04:00
return err
2014-11-21 22:26:09 -05:00
}
if dataUsed != 0 {
return fmt . Errorf ( "Unable to take ownership of thin-pool (%s) that already has used data blocks" ,
devices . thinPoolDevice )
}
2015-07-23 00:42:28 +00:00
if transactionID != 0 {
return fmt . Errorf ( "Unable to take ownership of thin-pool (%s) with non-zero transaction ID" ,
2014-11-21 22:26:09 -05:00
devices . thinPoolDevice )
2014-10-24 19:25:24 -04:00
}
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Initializing base device-mapper thin volume" )
2013-09-04 11:18:23 +02:00
// Create initial device
2014-12-03 13:06:43 -05:00
info , err := devices . createRegisterDevice ( "" )
2013-09-04 11:18:23 +02:00
if err != nil {
2014-12-03 13:06:43 -05:00
return err
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Creating filesystem on base device-mapper thin volume" )
2013-09-04 11:18:23 +02:00
2015-04-26 18:50:25 +02:00
if err := devices . activateDeviceIfNeeded ( info ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
2013-10-02 20:18:15 -07:00
if err := devices . createFilesystem ( info ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
info . Initialized = true
2015-04-26 18:50:25 +02:00
if err := devices . saveMetadata ( info ) ; err != nil {
2013-09-04 11:18:23 +02:00
info . Initialized = false
return err
}
2015-06-16 21:12:27 -04:00
if err := devices . saveBaseDeviceUUID ( info ) ; err != nil {
return fmt . Errorf ( "Could not query and save base device UUID:%v" , err )
}
2013-09-04 11:18:23 +02:00
return nil
}
2013-10-03 21:00:16 +02:00
func setCloseOnExec ( name string ) {
2013-11-20 13:05:17 -08:00
if fileInfos , _ := ioutil . ReadDir ( "/proc/self/fd" ) ; fileInfos != nil {
2013-10-03 21:00:16 +02:00
for _ , i := range fileInfos {
2014-05-16 14:10:02 +02:00
link , _ := os . Readlink ( filepath . Join ( "/proc/self/fd" , i . Name ( ) ) )
2013-10-11 20:37:11 -07:00
if link == name {
2013-10-03 21:00:16 +02:00
fd , err := strconv . Atoi ( i . Name ( ) )
if err == nil {
2014-05-16 14:10:02 +02:00
syscall . CloseOnExec ( fd )
2013-10-03 21:00:16 +02:00
}
}
}
}
}
2015-07-23 00:42:28 +00: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-23 00:42:28 +00:00
if level > logLevel {
2015-04-02 16:47:14 -04:00
return
2013-10-10 15:24:39 +02: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 15:24:39 +02:00
}
2013-11-07 23:58:03 +00:00
func major ( device uint64 ) uint64 {
return ( device >> 8 ) & 0xfff
2013-10-18 11:38:21 +02:00
}
2013-11-07 23:58:03 +00:00
func minor ( device uint64 ) uint64 {
2013-10-18 11:38:21 +02:00
return ( device & 0xff ) | ( ( device >> 12 ) & 0xfff00 )
}
2015-07-23 00:42:28 +00:00
// ResizePool increases the size of the pool.
2013-11-18 12:12:04 +01: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 12:12:04 +01: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 12:12:04 +01:00
2014-05-16 14:10:02 +02:00
datafile , err := os . OpenFile ( datafilename , os . O_RDWR , 0 )
2013-11-18 12:12:04 +01:00
if datafile == nil {
return err
}
defer datafile . Close ( )
fi , err := datafile . Stat ( )
if fi == nil {
return err
}
if fi . Size ( ) > size {
return fmt . Errorf ( "Can't shrink file" )
}
2014-11-05 18:10:38 -05:00
dataloopback := devicemapper . FindLoopDeviceFor ( datafile )
2013-11-18 12:12:04 +01:00
if dataloopback == nil {
return fmt . Errorf ( "Unable to find loopback mount for: %s" , datafilename )
}
defer dataloopback . Close ( )
2014-05-16 14:10:02 +02:00
metadatafile , err := os . OpenFile ( metadatafilename , os . O_RDWR , 0 )
2013-11-18 12:12:04 +01:00
if metadatafile == nil {
return err
}
defer metadatafile . Close ( )
2014-11-05 18:10:38 -05:00
metadataloopback := devicemapper . FindLoopDeviceFor ( metadatafile )
2013-11-18 12:12:04 +01:00
if metadataloopback == nil {
return fmt . Errorf ( "Unable to find loopback mount for: %s" , metadatafilename )
}
defer metadataloopback . Close ( )
// Grow loopback file
if err := datafile . Truncate ( size ) ; err != nil {
return fmt . Errorf ( "Unable to grow loopback file: %s" , err )
}
// Reload size for loopback device
2014-11-05 18:10:38 -05:00
if err := devicemapper . LoopbackSetCapacity ( dataloopback ) ; err != nil {
2013-11-18 12:12:04 +01:00
return fmt . Errorf ( "Unable to update loopback capacity: %s" , err )
}
// Suspend the pool
2014-11-05 18:10:38 -05:00
if err := devicemapper . SuspendDevice ( devices . getPoolName ( ) ) ; err != nil {
2013-11-18 12:12:04 +01:00
return fmt . Errorf ( "Unable to suspend pool: %s" , err )
}
// 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 {
2013-11-18 12:12:04 +01:00
return fmt . Errorf ( "Unable to reload pool: %s" , err )
}
// Resume the pool
2014-11-05 18:10:38 -05:00
if err := devicemapper . ResumeDevice ( devices . getPoolName ( ) ) ; err != nil {
2013-11-18 12:12:04 +01:00
return fmt . Errorf ( "Unable to resume pool: %s" , err )
}
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-23 00:42:28 +00:00
devices . OpenTransactionID = devices . TransactionID
2014-12-03 13:06:43 -05:00
return nil
}
return err
}
2015-07-23 00:42:28 +00:00
json . Unmarshal ( jsonData , & devices . transaction )
2014-12-03 13:06:43 -05:00
return nil
}
func ( devices * DeviceSet ) saveTransactionMetaData ( ) error {
2015-07-23 00:42:28 +00:00
jsonData , err := json . Marshal ( & devices . transaction )
2014-12-03 13:06:43 -05:00
if err != nil {
return fmt . Errorf ( "Error encoding metadata to json: %s" , err )
}
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-07-23 00:42:28 +00:00
logrus . Debugf ( "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-23 00:42:28 +00:00
if err := devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , devices . DeviceID ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Unable to delete device: %s" , err )
2014-12-03 13:06:43 -05:00
}
2015-07-23 00:42:28 +00:00
dinfo := & devInfo { Hash : devices . DeviceIDHash }
2014-12-03 13:06:43 -05:00
if err := devices . removeMetadata ( dinfo ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Unable to remove metadata: %s" , err )
2014-12-03 13:06:43 -05:00
} else {
2015-07-23 00:42:28 +00:00
devices . markDeviceIDFree ( devices . DeviceID )
2014-12-03 13:06:43 -05:00
}
if err := devices . removeTransactionMetaData ( ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "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-23 00:42:28 +00: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-23 00:42:28 +00: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-23 00:42:28 +00:00
if devices . OpenTransactionID < devices . TransactionID {
logrus . Errorf ( "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-23 00:42:28 +00: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 {
return fmt . Errorf ( "Rolling back open transaction failed: %s" , err )
}
2015-07-23 00:42:28 +00: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 {
return fmt . Errorf ( "Error encoding metadata to json: %s" , err )
}
2014-11-07 16:53:34 -05:00
return devices . writeMetaFile ( jsonData , devices . deviceSetMetaFile ( ) )
2014-11-05 09:25:02 -05:00
}
2015-07-23 00:42:28 +00: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-02-28 18:04:10 +08:00
return fmt . Errorf ( "Error saving transaction metadata: %s" , err )
2014-12-03 13:06:43 -05:00
}
return nil
}
2015-07-23 00:42:28 +00: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-02-28 18:04:10 +08:00
return fmt . Errorf ( "Error saving transaction metadata: %s" , err )
2014-12-03 13:06:43 -05:00
}
return nil
}
func ( devices * DeviceSet ) closeTransaction ( ) error {
2015-07-23 00:42:28 +00:00
if err := devices . updatePoolTransactionID ( ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "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-23 00:42:28 +00: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-23 00:42:28 +00: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 )
logrus . Debugf ( "[devmapper]: Major:Minor for device: %s is:%v:%v" , file . Name ( ) , majorNum , minorNum )
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 {
logrus . Debugf ( "[devmapper]: Failed to open file %s" , filename )
return "" , 0 , 0 , err
}
defer file . Close ( )
loopbackDevice := devicemapper . FindLoopDeviceFor ( file )
if loopbackDevice == nil {
return "" , 0 , 0 , fmt . Errorf ( "[devmapper]: Unable to find loopback mount for: %s" , filename )
}
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
}
logrus . Debugf ( "[devmapper]: poolDataMajMin=%s poolMetaMajMin=%s\n" , poolDataMajMin , poolMetadataMajMin )
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
}
2013-11-18 11:33:07 +01:00
func ( devices * DeviceSet ) initDevmapper ( doInit bool ) error {
2014-11-05 18:10:38 -05:00
// give ourselves to libdm as a log handler
devicemapper . LogInit ( devices )
2013-10-10 15:24:39 +02:00
2015-04-21 18:14:59 -04:00
version , err := devicemapper . GetDriverVersion ( )
2014-03-27 17:45:02 +01:00
if err != nil {
// Can't even get driver version, assume not supported
return graphdriver . ErrNotSupported
}
2015-04-21 18:14:59 -04:00
if err := determineDriverCapabilities ( version ) ; err != nil {
return graphdriver . ErrNotSupported
}
// If user asked for deferred removal and both library and driver
// supports deferred removal use it.
2015-07-23 00:42:28 +00:00
if enableDeferredRemoval && driverDeferredRemovalSupport && devicemapper . LibraryDeferredRemovalSupport == true {
2015-04-21 18:14:59 -04:00
logrus . Debugf ( "devmapper: Deferred removal support enabled." )
devices . deferredRemove = true
}
2015-01-19 15:11:40 -05:00
// https://github.com/docker/docker/issues/4036
if supported := devicemapper . UdevSetSyncSupport ( true ) ; ! supported {
2015-07-06 18:21:44 -07:00
logrus . Warn ( "Udev sync is not supported. This will lead to unexpected behavior, data loss and errors. For more information, see https://docs.docker.com/reference/commandline/cli/#daemon-storage-driver-option" )
2015-01-19 15:11:40 -05:00
}
Simplify and fix os.MkdirAll() usage
TL;DR: check for IsExist(err) after a failed MkdirAll() is both
redundant and wrong -- so two reasons to remove it.
Quoting MkdirAll documentation:
> MkdirAll creates a directory named path, along with any necessary
> parents, and returns nil, or else returns an error. If path
> is already a directory, MkdirAll does nothing and returns nil.
This means two things:
1. If a directory to be created already exists, no error is returned.
2. If the error returned is IsExist (EEXIST), it means there exists
a non-directory with the same name as MkdirAll need to use for
directory. Example: we want to MkdirAll("a/b"), but file "a"
(or "a/b") already exists, so MkdirAll fails.
The above is a theory, based on quoted documentation and my UNIX
knowledge.
3. In practice, though, current MkdirAll implementation [1] returns
ENOTDIR in most of cases described in #2, with the exception when
there is a race between MkdirAll and someone else creating the
last component of MkdirAll argument as a file. In this very case
MkdirAll() will indeed return EEXIST.
Because of #1, IsExist check after MkdirAll is not needed.
Because of #2 and #3, ignoring IsExist error is just plain wrong,
as directory we require is not created. It's cleaner to report
the error now.
Note this error is all over the tree, I guess due to copy-paste,
or trying to follow the same usage pattern as for Mkdir(),
or some not quite correct examples on the Internet.
[v2: a separate aufs commit is merged into this one]
[1] https://github.com/golang/go/blob/f9ed2f75/src/os/path.go
Signed-off-by: Kir Kolyshkin <kir@openvz.org>
2015-07-29 16:49:05 -07:00
if err := os . MkdirAll ( devices . metadataDir ( ) , 0700 ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
2013-10-17 09:53:09 +02:00
// Set the device prefix from the device id and inode of the docker root dir
2013-10-17 01:46:28 +00:00
2014-05-16 14:10:02 +02:00
st , err := os . Stat ( devices . root )
2013-09-04 11:18:23 +02:00
if err != nil {
2013-10-17 09:53:09 +02:00
return fmt . Errorf ( "Error looking up dir %s: %s" , devices . root , err )
2013-09-04 11:18:23 +02:00
}
2014-05-16 14:10:02 +02:00
sysSt := st . Sys ( ) . ( * syscall . Stat_t )
2013-10-17 01:46:28 +00:00
// "reg-" stands for "regular file".
// In the future we might use "dev-" for "device file", etc.
2013-10-18 08:30:53 +00: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 11:39:47 +02:00
devices . devicePrefix = fmt . Sprintf ( "docker-%d:%d-%d" , major ( sysSt . Dev ) , minor ( sysSt . Dev ) , sysSt . Ino )
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Generated prefix: %s" , devices . devicePrefix )
2013-10-17 01:46:28 +00:00
2014-10-24 19:25:24 -04:00
// Check for the existence of the thin-pool device
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Checking for existence of the pool '%s'" , devices . getPoolName ( ) )
2014-11-05 18:10:38 -05:00
info , err := devicemapper . GetInfo ( devices . getPoolName ( ) )
2013-10-17 01:46:28 +00:00
if info == nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error device devicemapper.GetInfo: %s" , err )
2013-09-04 11:18:23 +02:00
return err
}
2013-10-17 01:46:28 +00:00
// It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files
// that are not Close-on-exec, and lxc-start will die if it inherits any unexpected files,
// so we add this badhack to make sure it closes itself
setCloseOnExec ( "/dev/mapper/control" )
2014-04-24 23:49:44 +02:00
// Make sure the sparse images exist in <root>/devicemapper/data and
// <root>/devicemapper/metadata
createdLoopback := false
2013-10-17 01:46:28 +00:00
// If the pool doesn't exist, create it
2014-10-24 19:25:24 -04:00
if info . Exists == 0 && devices . thinPoolDevice == "" {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Pool doesn't exist. Creating it." )
2013-10-17 15:04:14 -07:00
2014-03-28 15:36:55 +01:00
var (
dataFile * os . File
metadataFile * os . File
)
2014-04-24 23:49:44 +02:00
2014-03-28 15:36:55 +01:00
if devices . dataDevice == "" {
// Make sure the sparse images exist in <root>/devicemapper/data
2014-04-24 23:49:44 +02:00
2014-03-28 15:36:55 +01:00
hasData := devices . hasImage ( "data" )
2014-04-24 23:49:44 +02:00
2014-03-28 15:36:55 +01:00
if ! doInit && ! hasData {
return errors . New ( "Loopback data file not found" )
}
2014-04-24 23:49:44 +02:00
2014-03-28 15:36:55 +01:00
if ! hasData {
createdLoopback = true
}
data , err := devices . ensureImage ( "data" , devices . dataLoopbackSize )
if err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error device ensureImage (data): %s" , err )
2014-03-28 15:36:55 +01:00
return err
}
2014-11-05 18:10:38 -05:00
dataFile , err = devicemapper . AttachLoopDevice ( data )
2014-03-28 15:36:55 +01:00
if err != nil {
return err
}
2015-01-09 11:20:07 -05:00
devices . dataLoopFile = data
devices . dataDevice = dataFile . Name ( )
2014-03-28 15:36:55 +01:00
} else {
dataFile , err = os . OpenFile ( devices . dataDevice , os . O_RDWR , 0600 )
if err != nil {
return err
}
2013-10-17 01:46:28 +00:00
}
2014-06-05 15:13:43 -07:00
defer dataFile . Close ( )
2013-10-17 01:46:28 +00:00
2014-03-28 15:36:55 +01: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-03-26 23:22:04 +01:00
logrus . Debugf ( "Error device ensureImage (metadata): %s" , err )
2014-03-28 15:36:55 +01:00
return err
}
2014-11-05 18:10:38 -05:00
metadataFile , err = devicemapper . AttachLoopDevice ( metadata )
2014-03-28 15:36:55 +01:00
if err != nil {
return err
}
2015-01-09 11:20:07 -05:00
devices . metadataLoopFile = metadata
devices . metadataDevice = metadataFile . Name ( )
2014-03-28 15:36:55 +01:00
} else {
metadataFile , err = os . OpenFile ( devices . metadataDevice , os . O_RDWR , 0600 )
if err != nil {
return err
}
2013-10-17 01:46:28 +00:00
}
2014-06-05 15:13:43 -07:00
defer metadataFile . Close ( )
2013-10-17 01:46:28 +00:00
2014-11-05 18:10:38 -05:00
if err := devicemapper . CreatePool ( devices . getPoolName ( ) , dataFile , metadataFile , devices . thinpBlockSize ) ; err != nil {
2013-10-17 01:46:28 +00:00
return err
}
2013-09-04 11:18:23 +02: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.
if info . Exists != 0 && devices . thinPoolDevice == "" {
if err := devices . loadThinPoolLoopBackInfo ( ) ; err != nil {
logrus . Debugf ( "Failed to load thin pool loopback device information:%v" , err )
return err
}
}
2013-10-17 01:46:28 +00:00
// If we didn't just create the data or metadata image, we need to
2014-04-24 23:49:44 +02:00
// load the transaction id and migrate old metadata
2013-09-04 11:18:23 +02:00
if ! createdLoopback {
2015-04-26 18:50:25 +02:00
if err := devices . initMetaData ( ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
}
2015-07-23 00:42:28 +00: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 18:50:25 +02: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-17 01:46:28 +00:00
// Setup the base image
2013-11-18 11:33:07 +01:00
if doInit {
if err := devices . setupBaseImage ( ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error device setupBaseImage: %s" , err )
2013-11-18 11:33:07 +01:00
return err
}
2013-09-04 11:18:23 +02:00
}
return nil
}
2015-07-23 00:42:28 +00:00
// AddDevice adds a device and registers in the hash.
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) AddDevice ( hash , baseHash string ) error {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[deviceset] AddDevice(hash=%s basehash=%s)" , hash , baseHash )
defer logrus . Debugf ( "[deviceset] AddDevice(hash=%s basehash=%s) END" , hash , baseHash )
2014-12-03 13:06:43 -05:00
2014-04-01 10:45:40 +02:00
baseInfo , err := devices . lookupDevice ( baseHash )
if err != nil {
return err
2013-09-04 11:18:23 +02:00
}
2014-03-06 18:14:56 +01:00
baseInfo . lock . Lock ( )
defer baseInfo . lock . Unlock ( )
2014-04-01 11:28:21 +02:00
devices . Lock ( )
defer devices . Unlock ( )
if info , _ := devices . lookupDevice ( hash ) ; info != nil {
return fmt . Errorf ( "device %s already exists" , hash )
}
2014-12-03 13:06:43 -05:00
if err := devices . createRegisterSnapDevice ( hash , baseInfo ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
2014-12-03 13:06:43 -05:00
2013-09-04 11:18:23 +02:00
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) deleteDevice ( info * devInfo ) error {
2014-04-01 15:41:25 +02:00
if devices . doBlkDiscard {
// 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
// manually
if err := devices . activateDeviceIfNeeded ( info ) ; err == nil {
2014-11-05 18:10:38 -05:00
if err := devicemapper . BlockDeviceDiscard ( info . DevName ( ) ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error discarding block on device: %s (ignoring)" , err )
2014-04-01 15:41:25 +02:00
}
2013-12-17 09:12:44 +01:00
}
}
2014-11-05 18:10:38 -05:00
devinfo , _ := devicemapper . GetInfo ( info . Name ( ) )
2013-09-04 11:18:23 +02:00
if devinfo != nil && devinfo . Exists != 0 {
2015-04-02 16:47:14 -04:00
if err := devices . removeDevice ( info . Name ( ) ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error removing device: %s" , err )
2013-09-04 11:18:23 +02:00
return err
}
}
2015-07-23 00:42:28 +00:00
if err := devices . openTransaction ( info . Hash , info . DeviceID ) ; err != nil {
logrus . Debugf ( "Error opening transaction hash = %s deviceID = %d" , "" , info . DeviceID )
2014-12-03 13:06:43 -05:00
return err
}
2015-07-23 00:42:28 +00:00
if err := devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , info . DeviceID ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error deleting device: %s" , err )
2013-09-04 11:18:23 +02:00
return err
}
2015-07-23 00:42:28 +00:00
if err := devices . unregisterDevice ( info . DeviceID , info . Hash ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
2014-12-03 13:06:43 -05:00
if err := devices . closeTransaction ( ) ; err != nil {
return err
}
2015-07-23 00:42:28 +00:00
devices . markDeviceIDFree ( info . DeviceID )
2014-12-03 13:06:43 -05:00
2013-09-04 11:18:23 +02:00
return nil
}
2015-07-23 00:42:28 +00:00
// DeleteDevice deletes a device from the hash.
2014-02-04 17:13:00 +01:00
func ( devices * DeviceSet ) DeleteDevice ( hash string ) error {
2014-04-01 10:45:40 +02:00
info , err := devices . lookupDevice ( hash )
if err != nil {
return err
2014-03-06 18:14:56 +01:00
}
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 11:28:21 +02:00
devices . Lock ( )
defer devices . Unlock ( )
2014-04-01 10:24:26 +02:00
return devices . deleteDevice ( info )
2013-10-07 14:06:24 +02:00
}
2014-02-11 12:47:59 +01:00
func ( devices * DeviceSet ) deactivatePool ( ) error {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[devmapper] deactivatePool()" )
defer logrus . Debugf ( "[devmapper] deactivatePool END" )
2014-02-11 12:47:59 +01: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 12:47:59 +01:00
if err != nil {
return err
}
2014-11-05 18:10:38 -05:00
if d , err := devicemapper . GetDeps ( devname ) ; err == nil {
2014-09-25 10:57:37 -04:00
// Access to more Debug output
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[devmapper] devicemapper.GetDeps() %s: %#v" , devname , d )
2014-09-25 10:57:37 -04:00
}
2014-02-11 12:47:59 +01:00
if devinfo . Exists != 0 {
2014-11-05 18:10:38 -05:00
return devicemapper . RemoveDevice ( devname )
2014-02-11 12:47:59 +01:00
}
return nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) deactivateDevice ( info * devInfo ) error {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[devmapper] deactivateDevice(%s)" , info . Hash )
defer logrus . Debugf ( "[devmapper] deactivateDevice END(%s)" , info . Hash )
2014-02-11 12:55:40 +01:00
2014-11-05 18:10:38 -05:00
devinfo , err := devicemapper . GetInfo ( info . Name ( ) )
2013-09-04 11:18:23 +02: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 11:18:23 +02: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 21:37:53 +01:00
var err error
2015-04-02 16:47:14 -04: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 21:37:53 +01:00
if err == nil {
break
}
2014-11-05 18:10:38 -05:00
if err != devicemapper . ErrBusy {
2014-02-05 21:37:53 +01:00
return err
}
// If we see EBUSY it may be a transient error,
// sleep a bit a retry a few times.
2014-03-06 18:25:43 +01:00
devices . Unlock ( )
2015-04-02 16:47:14 -04:00
time . Sleep ( 100 * time . Millisecond )
2014-03-06 18:25:43 +01:00
devices . Lock ( )
2014-02-05 21:37:53 +01:00
}
2015-04-02 16:47:14 -04:00
return err
2013-10-16 23:26:37 +00:00
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) cancelDeferredRemoval ( info * devInfo ) error {
2015-04-21 18:14:59 -04:00
if ! devices . deferredRemove {
return nil
}
logrus . Debugf ( "[devmapper] cancelDeferredRemoval START(%s)" , info . Name ( ) )
2015-07-15 12:25:50 -07:00
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-23 00:42:28 +00:00
// Shutdown shuts down the device by unmounting the root.
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) Shutdown ( ) error {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[deviceset %s] Shutdown()" , devices . devicePrefix )
logrus . Debugf ( "[devmapper] Shutting down DeviceSet: %s" , devices . root )
defer logrus . Debugf ( "[deviceset %s] Shutdown() END" , devices . devicePrefix )
2013-11-20 12:49:01 -08:00
2015-07-23 00:42:28 +00:00
var devs [ ] * devInfo
2014-04-01 11:05:30 +02:00
devices . devicesLock . Lock ( )
2014-02-04 18:19:09 +01:00
for _ , info := range devices . Devices {
2014-04-01 11:05:30 +02:00
devs = append ( devs , info )
}
devices . devicesLock . Unlock ( )
for _ , info := range devs {
2014-03-06 18:14:56 +01:00
info . lock . Lock ( )
2014-02-04 18:19:09 +01:00
if info . mountCount > 0 {
2014-03-20 17:32:59 +01: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.
2014-05-16 14:10:02 +02:00
if err := syscall . Unmount ( info . mountPath , syscall . MNT_DETACH ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Shutdown unmounting %s, error: %s" , info . mountPath , err )
2013-09-09 13:47:29 +02:00
}
2014-03-06 18:14:56 +01:00
2014-04-01 11:28:21 +02:00
devices . Lock ( )
2014-04-01 10:31:34 +02:00
if err := devices . deactivateDevice ( info ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Shutdown deactivate %s , error: %s" , info . Hash , err )
2014-03-20 17:32:59 +01:00
}
2014-04-01 11:28:21 +02:00
devices . Unlock ( )
2013-09-09 13:47:29 +02:00
}
2014-03-20 17:32:59 +01:00
info . lock . Unlock ( )
2013-09-09 13:47:29 +02:00
}
2014-04-01 10:45:40 +02:00
info , _ := devices . lookupDevice ( "" )
2014-04-01 10:31:34 +02:00
if info != nil {
2014-04-01 11:28:21 +02:00
info . lock . Lock ( )
devices . Lock ( )
2014-04-01 10:31:34 +02:00
if err := devices . deactivateDevice ( info ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Shutdown deactivate base , error: %s" , err )
2014-04-01 10:31:34 +02:00
}
2014-04-01 11:28:21 +02:00
devices . Unlock ( )
info . lock . Unlock ( )
2014-03-28 15:58:14 +01:00
}
2014-04-01 11:28:21 +02:00
devices . Lock ( )
2014-10-24 19:25:24 -04:00
if devices . thinPoolDevice == "" {
if err := devices . deactivatePool ( ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Shutdown deactivate pool , error: %s" , err )
2014-10-24 19:25:24 -04:00
}
2013-09-09 13:47:29 +02:00
}
2014-11-05 09:25:02 -05:00
devices . saveDeviceSetMetaData ( )
2014-04-01 11:28:21 +02:00
devices . Unlock ( )
2013-09-09 13:47:29 +02:00
return nil
}
2015-07-23 00:42:28 +00:00
// MountDevice mounts the device if not already mounted.
2014-04-17 23:47:27 +00:00
func ( devices * DeviceSet ) MountDevice ( hash , path , mountLabel string ) error {
2014-04-01 10:45:40 +02:00
info , err := devices . lookupDevice ( hash )
if err != nil {
return err
2014-02-06 23:08:17 +01:00
}
2014-02-04 18:19:09 +01:00
2014-03-06 18:14:56 +01:00
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 11:28:21 +02:00
devices . Lock ( )
defer devices . Unlock ( )
2014-02-04 18:19:09 +01:00
if info . mountCount > 0 {
if path != info . mountPath {
2015-03-02 17:06:38 +08:00
return fmt . Errorf ( "Trying to mount devmapper device in multiple places (%s, %s)" , info . mountPath , path )
2014-02-04 18:19:09 +01:00
}
2014-04-23 13:50:53 +02:00
info . mountCount ++
2014-02-04 18:19:09 +01:00
return nil
}
2014-04-01 10:21:53 +02:00
if err := devices . activateDeviceIfNeeded ( info ) ; err != nil {
2013-10-18 07:09:13 +00:00
return fmt . Errorf ( "Error activating devmapper device for '%s': %s" , hash , err )
2013-09-04 11:18:23 +02:00
}
2014-03-18 12:26:42 +01:00
fstype , err := ProbeFsType ( info . DevName ( ) )
if err != nil {
return err
}
2014-03-18 14:23:43 +01: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-07-23 00:42:28 +00:00
if err := syscall . Mount ( info . DevName ( ) , path , fstype , syscall . MS_MGC_VAL , options ) ; err != nil {
2013-10-18 07:09:13 +00:00
return fmt . Errorf ( "Error mounting '%s' on '%s': %s" , info . DevName ( ) , path , err )
2013-09-04 11:18:23 +02:00
}
2013-09-09 13:47:29 +02:00
2014-02-04 18:19:09 +01:00
info . mountCount = 1
info . mountPath = path
2013-09-09 13:47:29 +02:00
2014-04-24 23:49:44 +02:00
return nil
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
// UnmountDevice unmounts the device and removes it from hash.
2014-04-23 13:50:53 +02:00
func ( devices * DeviceSet ) UnmountDevice ( hash string ) error {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[devmapper] UnmountDevice(hash=%s)" , hash )
defer logrus . Debugf ( "[devmapper] UnmountDevice(hash=%s) END" , hash )
2013-10-07 14:06:24 +02:00
2014-04-01 10:45:40 +02:00
info , err := devices . lookupDevice ( hash )
if err != nil {
return err
2014-02-06 22:26:09 +01:00
}
2014-02-04 18:19:09 +01:00
2014-03-06 18:14:56 +01:00
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 11:28:21 +02:00
devices . Lock ( )
defer devices . Unlock ( )
2014-02-04 18:19:09 +01:00
if info . mountCount == 0 {
2015-01-12 13:40:00 -05:00
return fmt . Errorf ( "UnmountDevice: device not-mounted id %s" , hash )
2014-02-04 18:19:09 +01:00
}
info . mountCount --
if info . mountCount > 0 {
return nil
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[devmapper] Unmount(%s)" , info . mountPath )
2015-01-03 14:07:24 -08:00
if err := syscall . Unmount ( info . mountPath , syscall . MNT_DETACH ) ; err != nil {
2013-09-09 12:39:42 +02:00
return err
}
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "[devmapper] Unmount done" )
2014-03-20 17:32:59 +01:00
2014-04-01 10:31:34 +02:00
if err := devices . deactivateDevice ( info ) ; err != nil {
2013-10-16 23:06:07 +00:00
return err
}
2013-09-09 12:39:42 +02:00
2014-02-04 18:19:09 +01:00
info . mountPath = ""
2013-10-07 14:06:24 +02:00
2013-09-09 12:39:42 +02:00
return nil
}
2015-07-23 00:42:28 +00:00
// HasDevice returns true if the device is in the hash and mounted.
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) HasDevice ( hash string ) bool {
2013-10-07 14:06:24 +02:00
devices . Lock ( )
defer devices . Unlock ( )
2014-04-01 10:45:40 +02:00
info , _ := devices . lookupDevice ( hash )
return info != nil
2013-09-04 11:18:23 +02:00
}
2015-07-23 00:42:28 +00:00
// HasActivatedDevice return true if the device exists.
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) HasActivatedDevice ( hash string ) bool {
2014-04-01 10:45:40 +02:00
info , _ := devices . lookupDevice ( hash )
2013-09-19 20:38:06 +02:00
if info == nil {
return false
}
2014-03-06 18:14:56 +01:00
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 11:28:21 +02:00
devices . Lock ( )
defer devices . Unlock ( )
2014-11-05 18:10:38 -05:00
devinfo , _ := devicemapper . GetInfo ( info . Name ( ) )
2013-10-02 20:18:15 -07:00
return devinfo != nil && devinfo . Exists != 0
2013-09-19 20:38:06 +02:00
}
2015-07-23 00:42:28 +00:00
// List returns a list of device ids.
2013-11-18 11:33:07 +01:00
func ( devices * DeviceSet ) List ( ) [ ] string {
devices . Lock ( )
defer devices . Unlock ( )
2014-04-01 11:05:30 +02:00
devices . devicesLock . Lock ( )
2013-11-18 11:33:07 +01:00
ids := make ( [ ] string , len ( devices . Devices ) )
i := 0
for k := range devices . Devices {
ids [ i ] = k
i ++
}
2014-04-01 11:05:30 +02:00
devices . devicesLock . Unlock ( )
2013-11-18 11:33:07 +01:00
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 11:33:07 +01:00
if err != nil {
return
}
if _ , err = fmt . Sscanf ( params , "%d %d" , & mappedSectors , & highestMappedSector ) ; err == nil {
return
}
return
}
2015-07-23 00:42:28 +00:00
// GetDeviceStatus provides size, mapped sectors
2013-11-18 11:33:07 +01:00
func ( devices * DeviceSet ) GetDeviceStatus ( hash string ) ( * DevStatus , error ) {
2014-04-01 10:45:40 +02:00
info , err := devices . lookupDevice ( hash )
if err != nil {
return nil , err
2013-11-18 11:33:07 +01:00
}
2014-03-06 18:14:56 +01:00
info . lock . Lock ( )
defer info . lock . Unlock ( )
2014-04-01 11:28:21 +02:00
devices . Lock ( )
defer devices . Unlock ( )
2013-11-18 11:33:07 +01:00
status := & DevStatus {
2015-07-23 00:42:28 +00:00
DeviceID : info . DeviceID ,
2013-11-18 11:33:07 +01:00
Size : info . Size ,
2015-07-23 00:42:28 +00:00
TransactionID : info . TransactionID ,
2013-11-18 11:33:07 +01:00
}
2014-04-01 10:21:53 +02:00
if err := devices . activateDeviceIfNeeded ( info ) ; err != nil {
2013-11-18 11:33:07 +01:00
return nil , fmt . Errorf ( "Error activating devmapper device for '%s': %s" , hash , err )
}
2015-03-25 09:53:04 +01:00
sizeInSectors , mappedSectors , highestMappedSector , err := devices . deviceStatus ( info . DevName ( ) )
if err != nil {
2013-11-18 11:33:07 +01:00
return nil , err
}
2015-03-25 09:53:04 +01:00
status . SizeInSectors = sizeInSectors
status . MappedSectors = mappedSectors
status . HighestMappedSector = highestMappedSector
2013-11-18 11:33:07 +01:00
return status , nil
}
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) poolStatus ( ) ( totalSizeInSectors , transactionID , dataUsed , dataTotal , metadataUsed , metadataTotal uint64 , err error ) {
2013-11-18 11:33:07 +01:00
var params string
2014-11-05 18:10:38 -05:00
if _ , totalSizeInSectors , _ , params , err = devicemapper . GetStatus ( devices . getPoolName ( ) ) ; err == nil {
2015-07-23 00:42:28 +00:00
_ , err = fmt . Sscanf ( params , "%d %d/%d %d/%d" , & transactionID , & metadataUsed , & metadataTotal , & dataUsed , & dataTotal )
2013-11-18 11:33:07 +01:00
}
return
}
2015-03-02 02:11:15 +08: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 14:03:00 -08: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 14:03:00 -08:00
func ( devices * DeviceSet ) MetadataDevicePath ( ) string {
2015-01-09 11:20:07 -05:00
return devices . metadataDevice
}
2015-01-30 22:21:34 -08:00
func ( devices * DeviceSet ) getUnderlyingAvailableSpace ( loopFile string ) ( uint64 , error ) {
buf := new ( syscall . Statfs_t )
2015-04-26 18:50:25 +02:00
if err := syscall . Statfs ( loopFile , buf ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Warnf ( "Couldn't stat loopfile filesystem %v: %v" , loopFile , err )
2015-01-30 22:21:34 -08: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-03-26 23:22:04 +01:00
logrus . Warnf ( "Couldn't stat loopfile %v: %v" , loopFile , err )
2015-01-30 22:21:34 -08: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 07:17:13 +00:00
func ( devices * DeviceSet ) Status ( ) * Status {
2013-10-17 11:16:50 +02:00
devices . Lock ( )
defer devices . Unlock ( )
2013-11-07 23:58:03 +00:00
status := & Status { }
2013-10-17 11:16:50 +02: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
2013-10-17 11:16:50 +02:00
2013-11-18 11:33:07 +01:00
totalSizeInSectors , _ , dataUsed , dataTotal , metadataUsed , metadataTotal , err := devices . poolStatus ( )
2013-10-17 11:16:50 +02:00
if err == nil {
2013-11-18 11:33:07 +01:00
// Convert from blocks to bytes
blockSizeInSectors := totalSizeInSectors / dataTotal
2013-10-17 11:16:50 +02:00
2013-11-18 11:33:07 +01:00
status . Data . Used = dataUsed * blockSizeInSectors * 512
status . Data . Total = dataTotal * blockSizeInSectors * 512
2015-01-30 22:21:34 -08:00
status . Data . Available = status . Data . Total - status . Data . Used
2013-10-17 11:16:50 +02:00
2013-11-18 11:33:07 +01:00
// metadata blocks are always 4k
status . Metadata . Used = metadataUsed * 4096
status . Metadata . Total = metadataTotal * 4096
2015-01-30 22:21:34 -08:00
status . Metadata . Available = status . Metadata . Total - status . Metadata . Used
2013-11-18 11:33:07 +01:00
status . SectorSize = blockSizeInSectors * 512
2015-01-30 22:21:34 -08: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
}
}
2013-10-17 11:16:50 +02:00
}
return status
}
2015-06-15 14:05:10 -04:00
// Status returns the current status of this deviceset
2015-07-23 00:42:28 +00:00
func ( devices * DeviceSet ) exportDeviceMetadata ( hash string ) ( * deviceMetadata , error ) {
2015-06-15 14:05:10 -04:00
info , err := devices . lookupDevice ( hash )
if err != nil {
return nil , err
}
info . lock . Lock ( )
defer info . lock . Unlock ( )
2015-07-23 00:42:28 +00:00
metadata := & deviceMetadata { info . DeviceID , info . Size , info . Name ( ) }
2015-06-15 14:05:10 -04:00
return metadata , nil
}
2015-07-23 00:42:28 +00:00
// NewDeviceSet creates the device set based on the options provided.
2014-03-27 17:48:32 +01:00
func NewDeviceSet ( root string , doInit bool , options [ ] string ) ( * DeviceSet , error ) {
2014-11-05 18:10:38 -05:00
devicemapper . SetDevDir ( "/dev" )
2013-09-05 15:32:57 +02:00
2013-10-24 21:04:49 +02:00
devices := & DeviceSet {
2015-07-07 12:27:19 -07:00
root : root ,
2015-07-23 00:42:28 +00:00
metaData : metaData { Devices : make ( map [ string ] * devInfo ) } ,
dataLoopbackSize : defaultDataLoopbackSize ,
metaDataLoopbackSize : defaultMetaDataLoopbackSize ,
baseFsSize : defaultBaseFsSize ,
overrideUdevSyncCheck : defaultUdevSyncOverride ,
2015-07-07 12:27:19 -07:00
filesystem : "ext4" ,
doBlkDiscard : true ,
2015-07-23 00:42:28 +00:00
thinpBlockSize : defaultThinpBlockSize ,
deviceIDMap : make ( [ ] byte , deviceIDMapSz ) ,
2014-03-27 17:48:32 +01:00
}
2014-04-01 15:41:25 +02:00
foundBlkDiscard := false
2014-03-27 17:48:32 +01:00
for _ , option := range options {
2014-07-28 17:23:38 -07:00
key , val , err := parsers . ParseKeyValueOpt ( option )
2014-03-27 17:48:32 +01: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 17:48:32 +01:00
if err != nil {
return nil , err
}
devices . baseFsSize = uint64 ( size )
case "dm.loopdatasize" :
2014-06-23 15:36:08 -04:00
size , err := units . RAMInBytes ( val )
2014-03-27 17:48:32 +01: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 17:48:32 +01:00
if err != nil {
return nil , err
}
devices . metaDataLoopbackSize = size
2014-03-18 14:23:43 +01:00
case "dm.fs" :
if val != "ext4" && val != "xfs" {
return nil , fmt . Errorf ( "Unsupported filesystem %s\n" , val )
}
devices . filesystem = val
case "dm.mkfsarg" :
devices . mkfsArgs = append ( devices . mkfsArgs , val )
case "dm.mountopt" :
devices . mountOptions = joinMountOptions ( devices . mountOptions , val )
2014-03-28 15:36:55 +01: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 15:41:25 +02: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 12:27:19 -07: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-23 00:42:28 +00:00
enableDeferredRemoval , err = strconv . ParseBool ( val )
2015-04-21 18:14:59 -04:00
if err != nil {
return nil , err
}
2014-03-27 17:48:32 +01:00
default :
return nil , fmt . Errorf ( "Unknown option %s\n" , key )
}
2013-09-04 11:18:23 +02:00
}
2013-10-24 21:04:49 +02:00
2014-04-01 15:41:25 +02: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 15:41:25 +02:00
devices . doBlkDiscard = false
}
2013-11-18 11:33:07 +01:00
if err := devices . initDevmapper ( doInit ) ; err != nil {
2013-10-24 21:04:49 +02:00
return nil , err
}
return devices , nil
2013-09-04 11:18:23 +02:00
}