|
@@ -8,6 +8,8 @@ import (
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"io/ioutil"
|
|
|
+ "os"
|
|
|
+ "os/exec"
|
|
|
"path"
|
|
|
"path/filepath"
|
|
|
"strconv"
|
|
@@ -16,7 +18,9 @@ import (
|
|
|
"syscall"
|
|
|
"time"
|
|
|
|
|
|
+ "github.com/dotcloud/docker/daemon/graphdriver"
|
|
|
"github.com/dotcloud/docker/pkg/label"
|
|
|
+ "github.com/dotcloud/docker/pkg/units"
|
|
|
"github.com/dotcloud/docker/utils"
|
|
|
)
|
|
|
|
|
@@ -62,8 +66,18 @@ type DeviceSet struct {
|
|
|
devicePrefix string
|
|
|
TransactionId uint64
|
|
|
NewTransactionId uint64
|
|
|
- nextFreeDevice int
|
|
|
- sawBusy bool
|
|
|
+ nextDeviceId int
|
|
|
+
|
|
|
+ // Options
|
|
|
+ dataLoopbackSize int64
|
|
|
+ metaDataLoopbackSize int64
|
|
|
+ baseFsSize uint64
|
|
|
+ filesystem string
|
|
|
+ mountOptions string
|
|
|
+ mkfsArgs []string
|
|
|
+ dataDevice string
|
|
|
+ metadataDevice string
|
|
|
+ doBlkDiscard bool
|
|
|
}
|
|
|
|
|
|
type DiskUsage struct {
|
|
@@ -109,7 +123,19 @@ func (devices *DeviceSet) loopbackDir() string {
|
|
|
return path.Join(devices.root, "devicemapper")
|
|
|
}
|
|
|
|
|
|
-func (devices *DeviceSet) jsonFile() string {
|
|
|
+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)
|
|
|
+}
|
|
|
+
|
|
|
+func (devices *DeviceSet) oldMetadataFile() string {
|
|
|
return path.Join(devices.loopbackDir(), "json")
|
|
|
}
|
|
|
|
|
@@ -125,7 +151,7 @@ func (devices *DeviceSet) hasImage(name string) bool {
|
|
|
dirname := devices.loopbackDir()
|
|
|
filename := path.Join(dirname, name)
|
|
|
|
|
|
- _, err := osStat(filename)
|
|
|
+ _, err := os.Stat(filename)
|
|
|
return err == nil
|
|
|
}
|
|
|
|
|
@@ -137,16 +163,16 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
|
|
dirname := devices.loopbackDir()
|
|
|
filename := path.Join(dirname, name)
|
|
|
|
|
|
- if err := osMkdirAll(dirname, 0700); err != nil && !osIsExist(err) {
|
|
|
+ if err := os.MkdirAll(dirname, 0700); err != nil && !os.IsExist(err) {
|
|
|
return "", err
|
|
|
}
|
|
|
|
|
|
- if _, err := osStat(filename); err != nil {
|
|
|
- if !osIsNotExist(err) {
|
|
|
+ if _, err := os.Stat(filename); err != nil {
|
|
|
+ if !os.IsNotExist(err) {
|
|
|
return "", err
|
|
|
}
|
|
|
utils.Debugf("Creating loopback file %s for device-manage use", filename)
|
|
|
- file, err := osOpenFile(filename, osORdWr|osOCreate, 0600)
|
|
|
+ file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600)
|
|
|
if err != nil {
|
|
|
return "", err
|
|
|
}
|
|
@@ -159,26 +185,24 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
|
|
return filename, nil
|
|
|
}
|
|
|
|
|
|
-func (devices *DeviceSet) allocateDeviceId() int {
|
|
|
- // TODO: Add smarter reuse of deleted devices
|
|
|
- id := devices.nextFreeDevice
|
|
|
- devices.nextFreeDevice = devices.nextFreeDevice + 1
|
|
|
- return id
|
|
|
-}
|
|
|
-
|
|
|
func (devices *DeviceSet) allocateTransactionId() uint64 {
|
|
|
devices.NewTransactionId = devices.NewTransactionId + 1
|
|
|
return devices.NewTransactionId
|
|
|
}
|
|
|
|
|
|
-func (devices *DeviceSet) saveMetadata() error {
|
|
|
- devices.devicesLock.Lock()
|
|
|
- jsonData, err := json.Marshal(devices.MetaData)
|
|
|
- devices.devicesLock.Unlock()
|
|
|
+func (devices *DeviceSet) removeMetadata(info *DevInfo) error {
|
|
|
+ if err := os.RemoveAll(devices.metadataFile(info)); err != nil {
|
|
|
+ return fmt.Errorf("Error removing metadata file %s: %s", devices.metadataFile(info), err)
|
|
|
+ }
|
|
|
+ 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)
|
|
|
}
|
|
|
- tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json")
|
|
|
+ tmpFile, err := ioutil.TempFile(devices.metadataDir(), ".tmp")
|
|
|
if err != nil {
|
|
|
return fmt.Errorf("Error creating metadata file: %s", err)
|
|
|
}
|
|
@@ -196,7 +220,7 @@ func (devices *DeviceSet) saveMetadata() error {
|
|
|
if err := tmpFile.Close(); err != nil {
|
|
|
return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err)
|
|
|
}
|
|
|
- if err := osRename(tmpFile.Name(), devices.jsonFile()); err != nil {
|
|
|
+ if err := os.Rename(tmpFile.Name(), devices.metadataFile(info)); err != nil {
|
|
|
return fmt.Errorf("Error committing metadata file %s: %s", tmpFile.Name(), err)
|
|
|
}
|
|
|
|
|
@@ -214,7 +238,12 @@ func (devices *DeviceSet) lookupDevice(hash string) (*DevInfo, error) {
|
|
|
defer devices.devicesLock.Unlock()
|
|
|
info := devices.Devices[hash]
|
|
|
if info == nil {
|
|
|
- return nil, fmt.Errorf("Unknown device %s", hash)
|
|
|
+ info = devices.loadMetadata(hash)
|
|
|
+ if info == nil {
|
|
|
+ return nil, fmt.Errorf("Unknown device %s", hash)
|
|
|
+ }
|
|
|
+
|
|
|
+ devices.Devices[hash] = info
|
|
|
}
|
|
|
return info, nil
|
|
|
}
|
|
@@ -234,7 +263,7 @@ func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*Dev
|
|
|
devices.Devices[hash] = info
|
|
|
devices.devicesLock.Unlock()
|
|
|
|
|
|
- if err := devices.saveMetadata(); err != nil {
|
|
|
+ if err := devices.saveMetadata(info); err != nil {
|
|
|
// Try to remove unused device
|
|
|
devices.devicesLock.Lock()
|
|
|
delete(devices.Devices, hash)
|
|
@@ -258,63 +287,94 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *DevInfo) error {
|
|
|
func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
|
|
|
devname := info.DevName()
|
|
|
|
|
|
- err := execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname)
|
|
|
- if err != nil {
|
|
|
- err = execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname)
|
|
|
+ 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()
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ err = fmt.Errorf("Unsupported filesystem type %s", devices.filesystem)
|
|
|
}
|
|
|
if err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
+
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (devices *DeviceSet) loadMetaData() error {
|
|
|
- utils.Debugf("loadMetadata()")
|
|
|
- defer utils.Debugf("loadMetadata END")
|
|
|
+func (devices *DeviceSet) initMetaData() error {
|
|
|
_, _, _, params, err := getStatus(devices.getPoolName())
|
|
|
if err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
devices.NewTransactionId = devices.TransactionId
|
|
|
|
|
|
- jsonData, err := ioutil.ReadFile(devices.jsonFile())
|
|
|
- if err != nil && !osIsNotExist(err) {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
+ // Migrate old metadatafile
|
|
|
+
|
|
|
+ jsonData, err := ioutil.ReadFile(devices.oldMetadataFile())
|
|
|
+ if err != nil && !os.IsNotExist(err) {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- devices.MetaData.Devices = make(map[string]*DevInfo)
|
|
|
if jsonData != nil {
|
|
|
- if err := json.Unmarshal(jsonData, &devices.MetaData); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
+ m := MetaData{Devices: make(map[string]*DevInfo)}
|
|
|
+
|
|
|
+ if err := json.Unmarshal(jsonData, &m); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- for hash, d := range devices.Devices {
|
|
|
- d.Hash = hash
|
|
|
- d.devices = devices
|
|
|
+ for hash, info := range m.Devices {
|
|
|
+ info.Hash = hash
|
|
|
|
|
|
- if d.DeviceId >= devices.nextFreeDevice {
|
|
|
- devices.nextFreeDevice = d.DeviceId + 1
|
|
|
+ // If the transaction id is larger than the actual one we lost the device due to some crash
|
|
|
+ if info.TransactionId <= devices.TransactionId {
|
|
|
+ devices.saveMetadata(info)
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // If the transaction id is larger than the actual one we lost the device due to some crash
|
|
|
- if d.TransactionId > devices.TransactionId {
|
|
|
- utils.Debugf("Removing lost device %s with id %d", hash, d.TransactionId)
|
|
|
- delete(devices.Devices, hash)
|
|
|
+ if err := os.Rename(devices.oldMetadataFile(), devices.oldMetadataFile()+".migrated"); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+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
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the transaction id is larger than the actual one we lost the device due to some crash
|
|
|
+ if info.TransactionId > devices.TransactionId {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return info
|
|
|
+}
|
|
|
+
|
|
|
func (devices *DeviceSet) setupBaseImage() error {
|
|
|
oldInfo, _ := devices.lookupDevice("")
|
|
|
if oldInfo != nil && oldInfo.Initialized {
|
|
@@ -324,45 +384,42 @@ func (devices *DeviceSet) setupBaseImage() error {
|
|
|
if oldInfo != nil && !oldInfo.Initialized {
|
|
|
utils.Debugf("Removing uninitialized base image")
|
|
|
if err := devices.deleteDevice(oldInfo); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
utils.Debugf("Initializing base device-manager snapshot")
|
|
|
|
|
|
- id := devices.allocateDeviceId()
|
|
|
+ id := devices.nextDeviceId
|
|
|
|
|
|
// Create initial device
|
|
|
- if err := createDevice(devices.getPoolDevName(), id); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
+ if err := createDevice(devices.getPoolDevName(), &id); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
|
|
|
- info, err := devices.registerDevice(id, "", DefaultBaseFsSize)
|
|
|
+ // Ids are 24bit, so wrap around
|
|
|
+ devices.nextDeviceId = (id + 1) & 0xffffff
|
|
|
+
|
|
|
+ utils.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize)
|
|
|
+ info, err := devices.registerDevice(id, "", devices.baseFsSize)
|
|
|
if err != nil {
|
|
|
_ = deleteDevice(devices.getPoolDevName(), id)
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
utils.Debugf("Creating filesystem on base device-manager snapshot")
|
|
|
|
|
|
if err = devices.activateDeviceIfNeeded(info); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
if err := devices.createFilesystem(info); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
info.Initialized = true
|
|
|
- if err = devices.saveMetadata(); err != nil {
|
|
|
+ if err = devices.saveMetadata(info); err != nil {
|
|
|
info.Initialized = false
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
|
|
@@ -372,11 +429,11 @@ func (devices *DeviceSet) setupBaseImage() error {
|
|
|
func setCloseOnExec(name string) {
|
|
|
if fileInfos, _ := ioutil.ReadDir("/proc/self/fd"); fileInfos != nil {
|
|
|
for _, i := range fileInfos {
|
|
|
- link, _ := osReadlink(filepath.Join("/proc/self/fd", i.Name()))
|
|
|
+ link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name()))
|
|
|
if link == name {
|
|
|
fd, err := strconv.Atoi(i.Name())
|
|
|
if err == nil {
|
|
|
- sysCloseOnExec(fd)
|
|
|
+ syscall.CloseOnExec(fd)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -388,10 +445,6 @@ func (devices *DeviceSet) log(level int, file string, line int, dmError int, mes
|
|
|
return // Ignore _LOG_DEBUG
|
|
|
}
|
|
|
|
|
|
- if strings.Contains(message, "busy") {
|
|
|
- devices.sawBusy = true
|
|
|
- }
|
|
|
-
|
|
|
utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
|
}
|
|
|
|
|
@@ -408,7 +461,7 @@ func (devices *DeviceSet) ResizePool(size int64) error {
|
|
|
datafilename := path.Join(dirname, "data")
|
|
|
metadatafilename := path.Join(dirname, "metadata")
|
|
|
|
|
|
- datafile, err := osOpenFile(datafilename, osORdWr, 0)
|
|
|
+ datafile, err := os.OpenFile(datafilename, os.O_RDWR, 0)
|
|
|
if datafile == nil {
|
|
|
return err
|
|
|
}
|
|
@@ -429,7 +482,7 @@ func (devices *DeviceSet) ResizePool(size int64) error {
|
|
|
}
|
|
|
defer dataloopback.Close()
|
|
|
|
|
|
- metadatafile, err := osOpenFile(metadatafilename, osORdWr, 0)
|
|
|
+ metadatafile, err := os.OpenFile(metadatafilename, os.O_RDWR, 0)
|
|
|
if metadatafile == nil {
|
|
|
return err
|
|
|
}
|
|
@@ -472,39 +525,23 @@ func (devices *DeviceSet) ResizePool(size int64) error {
|
|
|
func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|
|
logInit(devices)
|
|
|
|
|
|
- // Make sure the sparse images exist in <root>/devicemapper/data and
|
|
|
- // <root>/devicemapper/metadata
|
|
|
-
|
|
|
- hasData := devices.hasImage("data")
|
|
|
- hasMetadata := devices.hasImage("metadata")
|
|
|
-
|
|
|
- if !doInit && !hasData {
|
|
|
- return errors.New("Loopback data file not found")
|
|
|
- }
|
|
|
-
|
|
|
- if !doInit && !hasMetadata {
|
|
|
- return errors.New("Loopback metadata file not found")
|
|
|
- }
|
|
|
-
|
|
|
- createdLoopback := !hasData || !hasMetadata
|
|
|
- data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
|
|
|
+ _, err := getDriverVersion()
|
|
|
if err != nil {
|
|
|
- utils.Debugf("Error device ensureImage (data): %s\n", err)
|
|
|
- return err
|
|
|
+ // Can't even get driver version, assume not supported
|
|
|
+ return graphdriver.ErrNotSupported
|
|
|
}
|
|
|
- metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
|
|
|
- if err != nil {
|
|
|
- utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
|
|
+
|
|
|
+ if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil && !os.IsExist(err) {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// Set the device prefix from the device id and inode of the docker root dir
|
|
|
|
|
|
- st, err := osStat(devices.root)
|
|
|
+ st, err := os.Stat(devices.root)
|
|
|
if err != nil {
|
|
|
return fmt.Errorf("Error looking up dir %s: %s", devices.root, err)
|
|
|
}
|
|
|
- sysSt := toSysStatT(st.Sys())
|
|
|
+ sysSt := st.Sys().(*syscall.Stat_t)
|
|
|
// "reg-" stands for "regular file".
|
|
|
// In the future we might use "dev-" for "device file", etc.
|
|
|
// docker-maj,min[-inode] stands for:
|
|
@@ -527,35 +564,91 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|
|
// so we add this badhack to make sure it closes itself
|
|
|
setCloseOnExec("/dev/mapper/control")
|
|
|
|
|
|
+ // Make sure the sparse images exist in <root>/devicemapper/data and
|
|
|
+ // <root>/devicemapper/metadata
|
|
|
+
|
|
|
+ createdLoopback := false
|
|
|
+
|
|
|
// If the pool doesn't exist, create it
|
|
|
if info.Exists == 0 {
|
|
|
utils.Debugf("Pool doesn't exist. Creating it.")
|
|
|
|
|
|
- dataFile, err := attachLoopDevice(data)
|
|
|
- if err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
- return err
|
|
|
+ var (
|
|
|
+ dataFile *os.File
|
|
|
+ metadataFile *os.File
|
|
|
+ )
|
|
|
+
|
|
|
+ if devices.dataDevice == "" {
|
|
|
+ // Make sure the sparse images exist in <root>/devicemapper/data
|
|
|
+
|
|
|
+ hasData := devices.hasImage("data")
|
|
|
+
|
|
|
+ if !doInit && !hasData {
|
|
|
+ return errors.New("Loopback data file not found")
|
|
|
+ }
|
|
|
+
|
|
|
+ if !hasData {
|
|
|
+ createdLoopback = true
|
|
|
+ }
|
|
|
+
|
|
|
+ data, err := devices.ensureImage("data", devices.dataLoopbackSize)
|
|
|
+ if err != nil {
|
|
|
+ utils.Debugf("Error device ensureImage (data): %s\n", err)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ dataFile, err = attachLoopDevice(data)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dataFile, err = os.OpenFile(devices.dataDevice, os.O_RDWR, 0600)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
}
|
|
|
defer dataFile.Close()
|
|
|
|
|
|
- metadataFile, err := attachLoopDevice(metadata)
|
|
|
- if err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
- return err
|
|
|
+ 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 {
|
|
|
+ utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ metadataFile, err = attachLoopDevice(metadata)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ metadataFile, err = os.OpenFile(devices.metadataDevice, os.O_RDWR, 0600)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
}
|
|
|
defer metadataFile.Close()
|
|
|
|
|
|
if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// If we didn't just create the data or metadata image, we need to
|
|
|
- // load the metadata from the existing file.
|
|
|
+ // load the transaction id and migrate old metadata
|
|
|
if !createdLoopback {
|
|
|
- if err = devices.loadMetaData(); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
+ if err = devices.initMetaData(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
@@ -587,13 +680,16 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
|
|
|
return fmt.Errorf("device %s already exists", hash)
|
|
|
}
|
|
|
|
|
|
- deviceId := devices.allocateDeviceId()
|
|
|
+ deviceId := devices.nextDeviceId
|
|
|
|
|
|
- if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
|
|
|
+ if err := createSnapDevice(devices.getPoolDevName(), &deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
|
|
|
utils.Debugf("Error creating snap device: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
+ // Ids are 24bit, so wrap around
|
|
|
+ devices.nextDeviceId = (deviceId + 1) & 0xffffff
|
|
|
+
|
|
|
if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
|
|
|
deleteDevice(devices.getPoolDevName(), deviceId)
|
|
|
utils.Debugf("Error registering device: %s\n", err)
|
|
@@ -603,12 +699,14 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
|
|
|
}
|
|
|
|
|
|
func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
|
|
|
- // 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 {
|
|
|
- if err := BlockDeviceDiscard(info.DevName()); err != nil {
|
|
|
- utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
|
|
|
+ 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 {
|
|
|
+ if err := BlockDeviceDiscard(info.DevName()); err != nil {
|
|
|
+ utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -620,14 +718,6 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if info.Initialized {
|
|
|
- info.Initialized = false
|
|
|
- if err := devices.saveMetadata(); err != nil {
|
|
|
- utils.Debugf("Error saving meta data: %s\n", err)
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
|
|
|
utils.Debugf("Error deleting device: %s\n", err)
|
|
|
return err
|
|
@@ -638,11 +728,11 @@ func (devices *DeviceSet) deleteDevice(info *DevInfo) error {
|
|
|
delete(devices.Devices, info.Hash)
|
|
|
devices.devicesLock.Unlock()
|
|
|
|
|
|
- if err := devices.saveMetadata(); err != nil {
|
|
|
+ if err := devices.removeMetadata(info); err != nil {
|
|
|
devices.devicesLock.Lock()
|
|
|
devices.Devices[info.Hash] = info
|
|
|
devices.devicesLock.Unlock()
|
|
|
- utils.Debugf("Error saving meta data: %s\n", err)
|
|
|
+ utils.Debugf("Error removing meta data: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
|
|
@@ -670,7 +760,6 @@ func (devices *DeviceSet) deactivatePool() error {
|
|
|
devname := devices.getPoolDevName()
|
|
|
devinfo, err := getInfo(devname)
|
|
|
if err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
if devinfo.Exists != 0 {
|
|
@@ -692,12 +781,10 @@ func (devices *DeviceSet) deactivateDevice(info *DevInfo) error {
|
|
|
|
|
|
devinfo, err := getInfo(info.Name())
|
|
|
if err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
if devinfo.Exists != 0 {
|
|
|
if err := devices.removeDeviceAndWait(info.Name()); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
@@ -711,12 +798,11 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
|
|
|
var err error
|
|
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
- devices.sawBusy = false
|
|
|
err = removeDevice(devname)
|
|
|
if err == nil {
|
|
|
break
|
|
|
}
|
|
|
- if !devices.sawBusy {
|
|
|
+ if err != ErrBusy {
|
|
|
return err
|
|
|
}
|
|
|
|
|
@@ -813,7 +899,7 @@ func (devices *DeviceSet) Shutdown() error {
|
|
|
// 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.
|
|
|
- if err := sysUnmount(info.mountPath, syscall.MNT_DETACH); err != nil {
|
|
|
+ if err := syscall.Unmount(info.mountPath, syscall.MNT_DETACH); err != nil {
|
|
|
utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
|
|
|
}
|
|
|
|
|
@@ -871,13 +957,26 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
|
|
return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
|
|
|
}
|
|
|
|
|
|
- var flags uintptr = sysMsMgcVal
|
|
|
+ var flags uintptr = syscall.MS_MGC_VAL
|
|
|
|
|
|
- mountOptions := label.FormatMountLabel("discard", mountLabel)
|
|
|
- err = sysMount(info.DevName(), path, "ext4", flags, mountOptions)
|
|
|
- if err != nil && err == sysEInval {
|
|
|
- mountOptions = label.FormatMountLabel("", mountLabel)
|
|
|
- err = sysMount(info.DevName(), path, "ext4", flags, mountOptions)
|
|
|
+ fstype, err := ProbeFsType(info.DevName())
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ 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))
|
|
|
+ if err != nil && err == syscall.EINVAL {
|
|
|
+ err = syscall.Mount(info.DevName(), path, fstype, flags, options)
|
|
|
}
|
|
|
if err != nil {
|
|
|
return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
|
@@ -886,7 +985,7 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
|
|
|
info.mountCount = 1
|
|
|
info.mountPath = path
|
|
|
|
|
|
- return devices.setInitialized(info)
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
func (devices *DeviceSet) UnmountDevice(hash string) error {
|
|
@@ -914,8 +1013,7 @@ func (devices *DeviceSet) UnmountDevice(hash string) error {
|
|
|
}
|
|
|
|
|
|
utils.Debugf("[devmapper] Unmount(%s)", info.mountPath)
|
|
|
- if err := sysUnmount(info.mountPath, 0); err != nil {
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
+ if err := syscall.Unmount(info.mountPath, 0); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
utils.Debugf("[devmapper] Unmount done")
|
|
@@ -937,14 +1035,6 @@ func (devices *DeviceSet) HasDevice(hash string) bool {
|
|
|
return info != nil
|
|
|
}
|
|
|
|
|
|
-func (devices *DeviceSet) HasInitializedDevice(hash string) bool {
|
|
|
- devices.Lock()
|
|
|
- defer devices.Unlock()
|
|
|
-
|
|
|
- info, _ := devices.lookupDevice(hash)
|
|
|
- return info != nil && info.Initialized
|
|
|
-}
|
|
|
-
|
|
|
func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
|
|
|
info, _ := devices.lookupDevice(hash)
|
|
|
if info == nil {
|
|
@@ -961,17 +1051,6 @@ func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
|
|
|
return devinfo != nil && devinfo.Exists != 0
|
|
|
}
|
|
|
|
|
|
-func (devices *DeviceSet) setInitialized(info *DevInfo) error {
|
|
|
- info.Initialized = true
|
|
|
- if err := devices.saveMetadata(); err != nil {
|
|
|
- info.Initialized = false
|
|
|
- utils.Debugf("\n--->Err: %s\n", err)
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
func (devices *DeviceSet) List() []string {
|
|
|
devices.Lock()
|
|
|
defer devices.Unlock()
|
|
@@ -1069,12 +1148,72 @@ func (devices *DeviceSet) Status() *Status {
|
|
|
return status
|
|
|
}
|
|
|
|
|
|
-func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) {
|
|
|
+func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) {
|
|
|
SetDevDir("/dev")
|
|
|
|
|
|
devices := &DeviceSet{
|
|
|
- root: root,
|
|
|
- MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
|
|
+ root: root,
|
|
|
+ MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
|
|
+ dataLoopbackSize: DefaultDataLoopbackSize,
|
|
|
+ metaDataLoopbackSize: DefaultMetaDataLoopbackSize,
|
|
|
+ baseFsSize: DefaultBaseFsSize,
|
|
|
+ filesystem: "ext4",
|
|
|
+ doBlkDiscard: true,
|
|
|
+ }
|
|
|
+
|
|
|
+ foundBlkDiscard := false
|
|
|
+ for _, option := range options {
|
|
|
+ key, val, err := utils.ParseKeyValueOpt(option)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ key = strings.ToLower(key)
|
|
|
+ switch key {
|
|
|
+ case "dm.basesize":
|
|
|
+ size, err := units.FromHumanSize(val)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ devices.baseFsSize = uint64(size)
|
|
|
+ case "dm.loopdatasize":
|
|
|
+ size, err := units.FromHumanSize(val)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ devices.dataLoopbackSize = size
|
|
|
+ case "dm.loopmetadatasize":
|
|
|
+ size, err := units.FromHumanSize(val)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ devices.metaDataLoopbackSize = size
|
|
|
+ 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)
|
|
|
+ case "dm.metadatadev":
|
|
|
+ devices.metadataDevice = val
|
|
|
+ case "dm.datadev":
|
|
|
+ devices.dataDevice = val
|
|
|
+ case "dm.blkdiscard":
|
|
|
+ foundBlkDiscard = true
|
|
|
+ devices.doBlkDiscard, err = strconv.ParseBool(val)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("Unknown option %s\n", key)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // By default, don't do blk discard hack on raw devices, its rarely useful and is expensive
|
|
|
+ if !foundBlkDiscard && devices.dataDevice != "" {
|
|
|
+ devices.doBlkDiscard = false
|
|
|
}
|
|
|
|
|
|
if err := devices.initDevmapper(doInit); err != nil {
|