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"
2014-07-24 22:25:29 +00:00
"github.com/docker/libcontainer/label"
2013-09-04 11:18:23 +02:00
)
2013-10-11 20:37:11 -07:00
var (
2013-10-16 20:42:50 +00:00
DefaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024
DefaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024
DefaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024
2015-03-17 10:44:42 -04:00
DefaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors
DefaultUdevSyncOverride bool = false
2014-12-03 13:06:43 -05:00
MaxDeviceId int = 0xffffff // 24 bit, pool limit
DeviceIdMapSz int = ( 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.
DMLogLevel int = devicemapper . LogLevelFatal
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
2014-12-03 13:06:43 -05:00
type Transaction struct {
2014-12-03 13:06:43 -05:00
OpenTransactionId uint64 ` json:"open_transaction_id" `
DeviceIdHash string ` json:"device_hash" `
DeviceId int ` json:"device_id" `
2014-12-03 13:06:43 -05:00
}
2013-09-04 11:18:23 +02:00
type DevInfo struct {
2014-12-12 11:01:46 -08:00
Hash string ` json:"-" `
DeviceId int ` json:"device_id" `
Size uint64 ` json:"size" `
TransactionId uint64 ` json:"transaction_id" `
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
}
type MetaData struct {
2014-06-12 09:15:53 +04:00
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
}
2013-10-18 00:07:34 +00:00
type DeviceSet struct {
2014-12-03 13:06:43 -05:00
MetaData ` json:"-" `
sync . Mutex ` json:"-" ` // Protects Devices map and serializes calls into libdevmapper
root string
devicePrefix string
TransactionId uint64 ` json:"-" `
NextDeviceId int ` json:"next_device_id" `
2014-12-03 13:06:43 -05:00
deviceIdMap [ ] byte
2014-03-27 17:48:32 +01:00
// Options
2015-03-17 10:44:42 -04: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
Transaction ` json:"-" `
overrideUdevSyncCheck bool
2013-09-04 11:18:23 +02:00
}
2013-10-17 11:16:50 +02:00
type DiskUsage struct {
2015-01-30 22:21:34 -08:00
Used uint64
Total uint64
Available uint64
2013-10-17 11:16:50 +02:00
}
type Status struct {
2015-01-19 16:28:02 -05:00
PoolName string
DataFile string // actual block device for data
DataLoopback string // loopback file, if used
MetadataFile string // actual block device for metadata
MetadataLoopback string // loopback file, if used
Data DiskUsage
Metadata DiskUsage
SectorSize uint64
UdevSyncSupported bool
2013-11-18 11:33:07 +01:00
}
type DevStatus struct {
DeviceId int
Size uint64
TransactionId uint64
SizeInSectors uint64
MappedSectors uint64
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
}
func ( info * DevInfo ) Name ( ) string {
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
}
func ( info * DevInfo ) DevName ( ) string {
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" )
}
func ( devices * DeviceSet ) metadataFile ( info * DevInfo ) string {
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 )
2014-05-16 14:10:02 +02:00
if err := os . MkdirAll ( dirname , 0700 ) ; err != nil && ! os . IsExist ( err ) {
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
if err = file . Truncate ( size ) ; err != nil {
2013-09-04 11:18:23 +02:00
return "" , err
}
}
return filename , nil
}
2013-10-18 00:07:34 +00:00
func ( devices * DeviceSet ) allocateTransactionId ( ) uint64 {
2014-12-03 13:06:43 -05:00
devices . OpenTransactionId = devices . TransactionId + 1
return devices . OpenTransactionId
2013-09-04 11:18:23 +02:00
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) updatePoolTransactionId ( ) error {
2014-12-03 13:06:43 -05:00
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
}
2014-12-03 13:06:43 -05:00
devices . TransactionId = devices . OpenTransactionId
2014-12-03 13:06:43 -05:00
return nil
}
2014-04-24 23:49:44 +02: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
}
func ( devices * DeviceSet ) saveMetadata ( info * DevInfo ) error {
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
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) markDeviceIdUsed ( deviceId int ) {
var mask byte
i := deviceId % 8
mask = 1 << uint ( i )
devices . deviceIdMap [ deviceId / 8 ] = devices . deviceIdMap [ deviceId / 8 ] | mask
}
func ( devices * DeviceSet ) markDeviceIdFree ( deviceId int ) {
var mask byte
i := deviceId % 8
mask = ^ ( 1 << uint ( i ) )
devices . deviceIdMap [ deviceId / 8 ] = devices . deviceIdMap [ deviceId / 8 ] & mask
}
func ( devices * DeviceSet ) isDeviceIdFree ( deviceId int ) bool {
var mask byte
i := deviceId % 8
mask = ( 1 << uint ( i ) )
if ( devices . deviceIdMap [ deviceId / 8 ] & mask ) != 0 {
return false
}
return true
}
2014-04-01 10:45:40 +02: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 )
}
if dinfo . DeviceId > MaxDeviceId {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Ignoring Invalid DeviceId=%d" , dinfo . DeviceId )
2014-12-03 13:06:43 -05:00
return nil
}
devices . Lock ( )
devices . markDeviceIdUsed ( dinfo . DeviceId )
devices . Unlock ( )
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Added deviceId=%d to DeviceIdMap" , dinfo . DeviceId )
2014-12-03 13:06:43 -05:00
return nil
}
func ( devices * DeviceSet ) constructDeviceIdMap ( ) error {
2015-03-26 23:22:04 +01:00
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 )
2014-12-03 13:06:43 -05:00
info := & DevInfo {
Hash : hash ,
DeviceId : id ,
}
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
}
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 )
2013-09-04 11:18:23 +02:00
info := & DevInfo {
Hash : hash ,
DeviceId : id ,
Size : size ,
2014-12-03 13:06:43 -05: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
}
2014-04-01 10:21:53 +02: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
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
}
2014-11-05 18:10:38 -05:00
return devicemapper . ActivateDevice ( devices . getPoolDevName ( ) , info . Name ( ) , info . DeviceId , info . Size )
2013-09-04 11:18:23 +02:00
}
2013-10-18 00:07:34 +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 {
2014-04-24 23:49:44 +02:00
m := MetaData { Devices : make ( map [ string ] * DevInfo ) }
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
}
_ , transactionId , _ , _ , _ , _ , err := devices . poolStatus ( )
if err != nil {
return err
}
devices . TransactionId = transactionId
2014-12-03 13:06:43 -05:00
if err := devices . constructDeviceIdMap ( ) ; err != nil {
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
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) incNextDeviceId ( ) {
// Ids are 24bit, so wrap around
2014-12-03 13:06:43 -05:00
devices . NextDeviceId = ( devices . NextDeviceId + 1 ) & MaxDeviceId
2014-12-03 13:06:43 -05:00
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) getNextFreeDeviceId ( ) ( int , error ) {
2014-12-03 13:06:43 -05:00
devices . incNextDeviceId ( )
2014-12-03 13:06:43 -05:00
for i := 0 ; i <= MaxDeviceId ; i ++ {
if devices . isDeviceIdFree ( devices . NextDeviceId ) {
devices . markDeviceIdUsed ( devices . NextDeviceId )
return devices . NextDeviceId , nil
}
devices . incNextDeviceId ( )
}
return 0 , fmt . Errorf ( "Unable to find a free device Id" )
2014-12-03 13:06:43 -05:00
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) createRegisterDevice ( hash string ) ( * DevInfo , error ) {
2014-12-03 13:06:43 -05:00
deviceId , err := devices . getNextFreeDeviceId ( )
if err != nil {
return nil , err
}
2014-12-03 13:06:43 -05:00
if err := devices . openTransaction ( hash , deviceId ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error opening transaction hash = %s deviceId = %d" , hash , deviceId )
2014-12-03 13:06:43 -05:00
devices . markDeviceIdFree ( deviceId )
return nil , err
}
2014-12-03 13:06:43 -05:00
for {
2014-12-03 13:06:43 -05:00
if err := devicemapper . CreateDevice ( devices . getPoolDevName ( ) , deviceId ) ; err != nil {
2014-12-03 13:06:43 -05:00
if devicemapper . DeviceIdExists ( err ) {
2014-12-03 13:06:43 -05:00
// Device Id already exists. This should not
// happen. Now we have a mechianism to find
// a free device Id. So something is not right.
// Give a warning and continue.
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Device Id %d exists in pool but it is supposed to be unused" , deviceId )
2014-12-03 13:06:43 -05:00
deviceId , err = devices . getNextFreeDeviceId ( )
if err != nil {
return nil , err
}
2014-12-03 13:06:43 -05:00
// Save new device id into transaction
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 )
2014-12-03 13:06:43 -05: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-03-26 23:22:04 +01:00
logrus . Debugf ( "Registering device (id %v) with FS size %v" , deviceId , devices . baseFsSize )
2014-12-03 13:06:43 -05:00
info , err := devices . registerDevice ( deviceId , hash , devices . baseFsSize , devices . OpenTransactionId )
2014-12-03 13:06:43 -05:00
if err != nil {
_ = devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceId )
2014-12-03 13:06:43 -05:00
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 {
2014-12-03 13:06:43 -05:00
devices . unregisterDevice ( deviceId , hash )
devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceId )
2014-12-03 13:06:43 -05:00
devices . markDeviceIdFree ( deviceId )
2014-12-03 13:06:43 -05:00
return nil , err
}
return info , nil
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) createRegisterSnapDevice ( hash string , baseInfo * DevInfo ) error {
2014-12-03 13:06:43 -05:00
deviceId , err := devices . getNextFreeDeviceId ( )
if err != nil {
return err
}
2014-12-03 13:06:43 -05:00
if err := devices . openTransaction ( hash , deviceId ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error opening transaction hash = %s deviceId = %d" , hash , deviceId )
2014-12-03 13:06:43 -05:00
devices . markDeviceIdFree ( deviceId )
return err
}
2014-12-03 13:06:43 -05:00
for {
2014-12-03 13:06:43 -05: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 ) {
2014-12-03 13:06:43 -05:00
// Device Id already exists. This should not
// happen. Now we have a mechianism to find
// a free device Id. So something is not right.
// Give a warning and continue.
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Device Id %d exists in pool but it is supposed to be unused" , deviceId )
2014-12-03 13:06:43 -05:00
deviceId , err = devices . getNextFreeDeviceId ( )
if err != nil {
return err
}
2014-12-03 13:06:43 -05:00
// Save new device id into transaction
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 )
2014-12-03 13:06:43 -05:00
devices . markDeviceIdFree ( deviceId )
2014-12-03 13:06:43 -05:00
return err
}
break
}
2014-12-03 13:06:43 -05:00
2014-12-03 13:06:43 -05:00
if _ , err := devices . registerDevice ( deviceId , hash , baseInfo . Size , devices . OpenTransactionId ) ; err != nil {
2014-12-03 13:06:43 -05:00
devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceId )
2014-12-03 13:06:43 -05:00
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 {
2014-12-03 13:06:43 -05:00
devices . unregisterDevice ( deviceId , hash )
devicemapper . DeleteDevice ( devices . getPoolDevName ( ) , deviceId )
2014-12-03 13:06:43 -05:00
devices . markDeviceIdFree ( deviceId )
2014-12-03 13:06:43 -05:00
return err
}
return nil
}
2014-04-24 23:49:44 +02:00
func ( devices * DeviceSet ) loadMetadata ( hash string ) * DevInfo {
info := & DevInfo { Hash : hash , devices : devices }
jsonData , err := ioutil . ReadFile ( devices . metadataFile ( info ) )
if err != nil {
return nil
}
if err := json . Unmarshal ( jsonData , & info ) ; err != nil {
return nil
}
return info
}
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 {
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 {
2014-11-21 22:26:09 -05:00
_ , transactionId , dataUsed , _ , _ , _ , err := devices . poolStatus ( )
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 )
}
if transactionId != 0 {
return fmt . Errorf ( "Unable to take ownership of thin-pool (%s) with non-zero transaction Id" ,
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
2014-04-01 10:21:53 +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
2014-04-24 23:49:44 +02:00
if err = devices . saveMetadata ( info ) ; err != nil {
2013-09-04 11:18:23 +02:00
info . Initialized = false
return err
}
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
}
}
}
}
}
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.
if level > DMLogLevel {
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 )
}
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 ) {
devices . OpenTransactionId = devices . TransactionId
return nil
}
return err
}
json . Unmarshal ( jsonData , & devices . Transaction )
return nil
}
func ( devices * DeviceSet ) saveTransactionMetaData ( ) error {
jsonData , err := json . Marshal ( & devices . Transaction )
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-03-26 23:22:04 +01: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.
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
}
dinfo := & DevInfo { Hash : devices . DeviceIdHash }
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 {
devices . markDeviceIdFree ( devices . DeviceId )
}
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
}
// If there was open transaction but pool transaction Id is same
// as open transaction Id, nothing to roll back.
if devices . TransactionId == devices . OpenTransactionId {
return nil
}
// If open transaction Id is less than pool transaction Id, something
// is wrong. Bail out.
if devices . OpenTransactionId < devices . TransactionId {
2015-03-26 23:22:04 +01:00
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
}
// Pool transaction Id is not same as open transaction. There is
// a transaction which was not completed.
if err := devices . rollbackTransaction ( ) ; err != nil {
return fmt . Errorf ( "Rolling back open transaction failed: %s" , err )
}
devices . OpenTransactionId = devices . TransactionId
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
}
2014-12-03 13:06:43 -05:00
func ( devices * DeviceSet ) openTransaction ( hash string , DeviceId int ) error {
devices . allocateTransactionId ( )
devices . DeviceIdHash = hash
devices . DeviceId = DeviceId
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 ) refreshTransaction ( DeviceId int ) error {
devices . DeviceId = DeviceId
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 {
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
}
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
2014-11-05 18:10:38 -05:00
_ , 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-01-19 15:11:40 -05:00
// https://github.com/docker/docker/issues/4036
if supported := devicemapper . UdevSetSyncSupport ( true ) ; ! supported {
2015-03-17 10:44:42 -04:00
logrus . Errorf ( "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" )
if ! devices . overrideUdevSyncCheck {
return graphdriver . ErrNotSupported
}
2015-01-19 15:11:40 -05:00
}
2014-05-16 14:10:02 +02:00
if err := os . MkdirAll ( devices . metadataDir ( ) , 0700 ) ; err != nil && ! os . IsExist ( err ) {
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
}
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 {
2014-04-24 23:49:44 +02:00
if err = devices . initMetaData ( ) ; err != nil {
2013-09-04 11:18:23 +02:00
return err
}
}
2015-03-02 02:11:15 +08: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.
2014-11-07 16:53:34 -05:00
if err = devices . loadDeviceSetMetaData ( ) ; err != nil {
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
}
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
}
2014-04-01 10:24:26 +02: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
}
}
2014-12-03 13:06:43 -05:00
if err := devices . openTransaction ( info . Hash , info . DeviceId ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Error opening transaction hash = %s deviceId = %d" , "" , info . DeviceId )
2014-12-03 13:06:43 -05:00
return err
}
2014-11-05 18:10:38 -05: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
}
2014-12-03 13:06:43 -05: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
}
2014-12-03 13:06:43 -05:00
devices . markDeviceIdFree ( info . DeviceId )
2013-09-04 11:18:23 +02:00
return nil
}
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
}
2014-04-01 10:31:34 +02: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
}
if devinfo . Exists != 0 {
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
}
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
2014-04-01 11:05:30 +02:00
var devs [ ] * DevInfo
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
}
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-05-16 14:10:02 +02:00
var flags uintptr = syscall . MS_MGC_VAL
2013-10-17 16:14:53 +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 ) )
err = syscall . Mount ( info . DevName ( ) , path , fstype , flags , joinMountOptions ( "discard" , options ) )
2014-05-16 14:10:02 +02:00
if err != nil && err == syscall . EINVAL {
2014-03-18 14:23:43 +01:00
err = syscall . Mount ( info . DevName ( ) , path , fstype , flags , options )
2013-09-04 11:18:23 +02:00
}
if 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
}
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
}
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
}
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
}
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
}
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 {
DeviceId : info . DeviceId ,
Size : info . Size ,
TransactionId : info . TransactionId ,
}
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
}
func ( devices * DeviceSet ) poolStatus ( ) ( totalSizeInSectors , transactionId , dataUsed , dataTotal , metadataUsed , metadataTotal uint64 , err error ) {
var params string
2014-11-05 18:10:38 -05:00
if _ , totalSizeInSectors , _ , params , err = devicemapper . GetStatus ( devices . getPoolName ( ) ) ; err == nil {
2013-11-18 11:33:07 +01:00
_ , err = fmt . Sscanf ( params , "%d %d/%d %d/%d" , & transactionId , & metadataUsed , & metadataTotal , & dataUsed , & dataTotal )
}
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 )
err := syscall . Statfs ( loopFile , buf )
if 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 ( )
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
}
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-03-17 10:44:42 -04:00
root : root ,
MetaData : MetaData { Devices : make ( map [ string ] * DevInfo ) } ,
dataLoopbackSize : DefaultDataLoopbackSize ,
metaDataLoopbackSize : DefaultMetaDataLoopbackSize ,
baseFsSize : DefaultBaseFsSize ,
overrideUdevSyncCheck : DefaultUdevSyncOverride ,
filesystem : "ext4" ,
doBlkDiscard : true ,
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-03-17 10:44:42 -04:00
case "dm.override_udev_sync_check" :
devices . overrideUdevSyncCheck , err = strconv . ParseBool ( val )
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
}