| // +build linux |
| |
| package devmapper |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path" |
| |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/daemon/graphdriver" |
| "github.com/docker/docker/pkg/devicemapper" |
| "github.com/docker/docker/pkg/mount" |
| "github.com/docker/docker/pkg/units" |
| ) |
| |
| func init() { |
| graphdriver.Register("devicemapper", Init) |
| } |
| |
| // Placeholder interfaces, to be replaced |
| // at integration. |
| |
| // End of placeholder interfaces. |
| |
| type Driver struct { |
| *DeviceSet |
| home string |
| } |
| |
| var backingFs = "<unknown>" |
| |
| func Init(home string, options []string) (graphdriver.Driver, error) { |
| fsMagic, err := graphdriver.GetFSMagic(home) |
| if err != nil { |
| return nil, err |
| } |
| if fsName, ok := graphdriver.FsNames[fsMagic]; ok { |
| backingFs = fsName |
| } |
| |
| deviceSet, err := NewDeviceSet(home, true, options) |
| if err != nil { |
| return nil, err |
| } |
| |
| if err := mount.MakePrivate(home); err != nil { |
| return nil, err |
| } |
| |
| d := &Driver{ |
| DeviceSet: deviceSet, |
| home: home, |
| } |
| |
| return graphdriver.NaiveDiffDriver(d), nil |
| } |
| |
| func (d *Driver) String() string { |
| return "devicemapper" |
| } |
| |
| func (d *Driver) Status() [][2]string { |
| s := d.DeviceSet.Status() |
| |
| status := [][2]string{ |
| {"Pool Name", s.PoolName}, |
| {"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(float64(s.SectorSize)))}, |
| {"Backing Filesystem", backingFs}, |
| {"Data file", s.DataFile}, |
| {"Metadata file", s.MetadataFile}, |
| {"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))}, |
| {"Data Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Total)))}, |
| {"Data Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Available)))}, |
| {"Metadata Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Used)))}, |
| {"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Total)))}, |
| {"Metadata Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Available)))}, |
| {"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)}, |
| {"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)}, |
| } |
| if len(s.DataLoopback) > 0 { |
| status = append(status, [2]string{"Data loop file", s.DataLoopback}) |
| } |
| if len(s.MetadataLoopback) > 0 { |
| status = append(status, [2]string{"Metadata loop file", s.MetadataLoopback}) |
| } |
| if vStr, err := devicemapper.GetLibraryVersion(); err == nil { |
| status = append(status, [2]string{"Library Version", vStr}) |
| } |
| return status |
| } |
| |
| func (d *Driver) Cleanup() error { |
| err := d.DeviceSet.Shutdown() |
| |
| if err2 := mount.Unmount(d.home); err == nil { |
| err = err2 |
| } |
| |
| return err |
| } |
| |
| func (d *Driver) Create(id, parent string) error { |
| if err := d.DeviceSet.AddDevice(id, parent); err != nil { |
| return err |
| } |
| |
| return nil |
| } |
| |
| func (d *Driver) Remove(id string) error { |
| if !d.DeviceSet.HasDevice(id) { |
| // Consider removing a non-existing device a no-op |
| // This is useful to be able to progress on container removal |
| // if the underlying device has gone away due to earlier errors |
| return nil |
| } |
| |
| // This assumes the device has been properly Get/Put:ed and thus is unmounted |
| if err := d.DeviceSet.DeleteDevice(id); err != nil { |
| return err |
| } |
| |
| mp := path.Join(d.home, "mnt", id) |
| if err := os.RemoveAll(mp); err != nil && !os.IsNotExist(err) { |
| return err |
| } |
| |
| return nil |
| } |
| |
| func (d *Driver) Get(id, mountLabel string) (string, error) { |
| mp := path.Join(d.home, "mnt", id) |
| |
| // Create the target directories if they don't exist |
| if err := os.MkdirAll(mp, 0755); err != nil && !os.IsExist(err) { |
| return "", err |
| } |
| |
| // Mount the device |
| if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil { |
| return "", err |
| } |
| |
| rootFs := path.Join(mp, "rootfs") |
| if err := os.MkdirAll(rootFs, 0755); err != nil && !os.IsExist(err) { |
| d.DeviceSet.UnmountDevice(id) |
| return "", err |
| } |
| |
| idFile := path.Join(mp, "id") |
| if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) { |
| // Create an "id" file with the container/image id in it to help reconscruct this in case |
| // of later problems |
| if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil { |
| d.DeviceSet.UnmountDevice(id) |
| return "", err |
| } |
| } |
| |
| return rootFs, nil |
| } |
| |
| func (d *Driver) Put(id string) error { |
| err := d.DeviceSet.UnmountDevice(id) |
| if err != nil { |
| logrus.Errorf("Error unmounting device %s: %s", id, err) |
| } |
| return err |
| } |
| |
| func (d *Driver) Exists(id string) bool { |
| return d.DeviceSet.HasDevice(id) |
| } |