| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,320 +0,0 @@ |
| 1 |
-/* |
|
| 2 |
- |
|
| 3 |
-aufs driver directory structure |
|
| 4 |
- |
|
| 5 |
-. |
|
| 6 |
-├── layers // Metadata of layers |
|
| 7 |
-│ ├── 1 |
|
| 8 |
-│ ├── 2 |
|
| 9 |
-│ └── 3 |
|
| 10 |
-├── diffs // Content of the layer |
|
| 11 |
-│ ├── 1 // Contains layers that need to be mounted for the id |
|
| 12 |
-│ ├── 2 |
|
| 13 |
-│ └── 3 |
|
| 14 |
-└── mnt // Mount points for the rw layers to be mounted |
|
| 15 |
- ├── 1 |
|
| 16 |
- ├── 2 |
|
| 17 |
- └── 3 |
|
| 18 |
- |
|
| 19 |
-*/ |
|
| 20 |
- |
|
| 21 |
-package aufs |
|
| 22 |
- |
|
| 23 |
-import ( |
|
| 24 |
- "bufio" |
|
| 25 |
- "fmt" |
|
| 26 |
- "github.com/dotcloud/docker/archive" |
|
| 27 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 28 |
- "github.com/dotcloud/docker/utils" |
|
| 29 |
- "log" |
|
| 30 |
- "os" |
|
| 31 |
- "os/exec" |
|
| 32 |
- "path" |
|
| 33 |
- "strings" |
|
| 34 |
-) |
|
| 35 |
- |
|
| 36 |
-func init() {
|
|
| 37 |
- graphdriver.Register("aufs", Init)
|
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-type AufsDriver struct {
|
|
| 41 |
- root string |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-// New returns a new AUFS driver. |
|
| 45 |
-// An error is returned if AUFS is not supported. |
|
| 46 |
-func Init(root string) (graphdriver.Driver, error) {
|
|
| 47 |
- // Try to load the aufs kernel module |
|
| 48 |
- if err := supportsAufs(); err != nil {
|
|
| 49 |
- return nil, err |
|
| 50 |
- } |
|
| 51 |
- paths := []string{
|
|
| 52 |
- "mnt", |
|
| 53 |
- "diff", |
|
| 54 |
- "layers", |
|
| 55 |
- } |
|
| 56 |
- |
|
| 57 |
- // Create the root aufs driver dir and return |
|
| 58 |
- // if it already exists |
|
| 59 |
- // If not populate the dir structure |
|
| 60 |
- if err := os.MkdirAll(root, 0755); err != nil {
|
|
| 61 |
- if os.IsExist(err) {
|
|
| 62 |
- return &AufsDriver{root}, nil
|
|
| 63 |
- } |
|
| 64 |
- return nil, err |
|
| 65 |
- } |
|
| 66 |
- |
|
| 67 |
- for _, p := range paths {
|
|
| 68 |
- if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
|
|
| 69 |
- return nil, err |
|
| 70 |
- } |
|
| 71 |
- } |
|
| 72 |
- return &AufsDriver{root}, nil
|
|
| 73 |
-} |
|
| 74 |
- |
|
| 75 |
-// Return a nil error if the kernel supports aufs |
|
| 76 |
-// We cannot modprobe because inside dind modprobe fails |
|
| 77 |
-// to run |
|
| 78 |
-func supportsAufs() error {
|
|
| 79 |
- // We can try to modprobe aufs first before looking at |
|
| 80 |
- // proc/filesystems for when aufs is supported |
|
| 81 |
- exec.Command("modprobe", "aufs").Run()
|
|
| 82 |
- |
|
| 83 |
- f, err := os.Open("/proc/filesystems")
|
|
| 84 |
- if err != nil {
|
|
| 85 |
- return err |
|
| 86 |
- } |
|
| 87 |
- defer f.Close() |
|
| 88 |
- |
|
| 89 |
- s := bufio.NewScanner(f) |
|
| 90 |
- for s.Scan() {
|
|
| 91 |
- if strings.Contains(s.Text(), "aufs") {
|
|
| 92 |
- return nil |
|
| 93 |
- } |
|
| 94 |
- } |
|
| 95 |
- return fmt.Errorf("AUFS was not found in /proc/filesystems")
|
|
| 96 |
-} |
|
| 97 |
- |
|
| 98 |
-func (a *AufsDriver) rootPath() string {
|
|
| 99 |
- return a.root |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-func (a *AufsDriver) String() string {
|
|
| 103 |
- return "aufs" |
|
| 104 |
-} |
|
| 105 |
- |
|
| 106 |
-func (d *AufsDriver) Status() [][2]string {
|
|
| 107 |
- return nil |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-// Three folders are created for each id |
|
| 111 |
-// mnt, layers, and diff |
|
| 112 |
-func (a *AufsDriver) Create(id, parent string) error {
|
|
| 113 |
- if err := a.createDirsFor(id); err != nil {
|
|
| 114 |
- return err |
|
| 115 |
- } |
|
| 116 |
- // Write the layers metadata |
|
| 117 |
- f, err := os.Create(path.Join(a.rootPath(), "layers", id)) |
|
| 118 |
- if err != nil {
|
|
| 119 |
- return err |
|
| 120 |
- } |
|
| 121 |
- defer f.Close() |
|
| 122 |
- |
|
| 123 |
- if parent != "" {
|
|
| 124 |
- ids, err := getParentIds(a.rootPath(), parent) |
|
| 125 |
- if err != nil {
|
|
| 126 |
- return err |
|
| 127 |
- } |
|
| 128 |
- |
|
| 129 |
- if _, err := fmt.Fprintln(f, parent); err != nil {
|
|
| 130 |
- return err |
|
| 131 |
- } |
|
| 132 |
- for _, i := range ids {
|
|
| 133 |
- if _, err := fmt.Fprintln(f, i); err != nil {
|
|
| 134 |
- return err |
|
| 135 |
- } |
|
| 136 |
- } |
|
| 137 |
- } |
|
| 138 |
- return nil |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func (a *AufsDriver) createDirsFor(id string) error {
|
|
| 142 |
- paths := []string{
|
|
| 143 |
- "mnt", |
|
| 144 |
- "diff", |
|
| 145 |
- } |
|
| 146 |
- |
|
| 147 |
- for _, p := range paths {
|
|
| 148 |
- if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
|
|
| 149 |
- return err |
|
| 150 |
- } |
|
| 151 |
- } |
|
| 152 |
- return nil |
|
| 153 |
-} |
|
| 154 |
- |
|
| 155 |
-// Unmount and remove the dir information |
|
| 156 |
-func (a *AufsDriver) Remove(id string) error {
|
|
| 157 |
- // Make sure the dir is umounted first |
|
| 158 |
- if err := a.unmount(id); err != nil {
|
|
| 159 |
- return err |
|
| 160 |
- } |
|
| 161 |
- tmpDirs := []string{
|
|
| 162 |
- "mnt", |
|
| 163 |
- "diff", |
|
| 164 |
- } |
|
| 165 |
- |
|
| 166 |
- // Remove the dirs atomically |
|
| 167 |
- for _, p := range tmpDirs {
|
|
| 168 |
- // We need to use a temp dir in the same dir as the driver so Rename |
|
| 169 |
- // does not fall back to the slow copy if /tmp and the driver dir |
|
| 170 |
- // are on different devices |
|
| 171 |
- tmp := path.Join(a.rootPath(), "tmp", p, id) |
|
| 172 |
- if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 173 |
- return err |
|
| 174 |
- } |
|
| 175 |
- realPath := path.Join(a.rootPath(), p, id) |
|
| 176 |
- if err := os.Rename(realPath, tmp); err != nil {
|
|
| 177 |
- return err |
|
| 178 |
- } |
|
| 179 |
- defer os.RemoveAll(tmp) |
|
| 180 |
- } |
|
| 181 |
- |
|
| 182 |
- // Remove the layers file for the id |
|
| 183 |
- return os.Remove(path.Join(a.rootPath(), "layers", id)) |
|
| 184 |
-} |
|
| 185 |
- |
|
| 186 |
-// Return the rootfs path for the id |
|
| 187 |
-// This will mount the dir at it's given path |
|
| 188 |
-func (a *AufsDriver) Get(id string) (string, error) {
|
|
| 189 |
- ids, err := getParentIds(a.rootPath(), id) |
|
| 190 |
- if err != nil {
|
|
| 191 |
- if !os.IsNotExist(err) {
|
|
| 192 |
- return "", err |
|
| 193 |
- } |
|
| 194 |
- ids = []string{}
|
|
| 195 |
- } |
|
| 196 |
- |
|
| 197 |
- // If a dir does not have a parent ( no layers )do not try to mount |
|
| 198 |
- // just return the diff path to the data |
|
| 199 |
- out := path.Join(a.rootPath(), "diff", id) |
|
| 200 |
- if len(ids) > 0 {
|
|
| 201 |
- out = path.Join(a.rootPath(), "mnt", id) |
|
| 202 |
- if err := a.mount(id); err != nil {
|
|
| 203 |
- return "", err |
|
| 204 |
- } |
|
| 205 |
- } |
|
| 206 |
- return out, nil |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-// Returns an archive of the contents for the id |
|
| 210 |
-func (a *AufsDriver) Diff(id string) (archive.Archive, error) {
|
|
| 211 |
- return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
|
| 212 |
- Recursive: true, |
|
| 213 |
- Compression: archive.Uncompressed, |
|
| 214 |
- }) |
|
| 215 |
-} |
|
| 216 |
- |
|
| 217 |
-func (a *AufsDriver) ApplyDiff(id string, diff archive.Archive) error {
|
|
| 218 |
- return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil) |
|
| 219 |
-} |
|
| 220 |
- |
|
| 221 |
-// Returns the size of the contents for the id |
|
| 222 |
-func (a *AufsDriver) Size(id string) (int64, error) {
|
|
| 223 |
- return utils.TreeSize(path.Join(a.rootPath(), "diff", id)) |
|
| 224 |
-} |
|
| 225 |
- |
|
| 226 |
-func (a *AufsDriver) Changes(id string) ([]archive.Change, error) {
|
|
| 227 |
- layers, err := a.getParentLayerPaths(id) |
|
| 228 |
- if err != nil {
|
|
| 229 |
- return nil, err |
|
| 230 |
- } |
|
| 231 |
- return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) |
|
| 232 |
-} |
|
| 233 |
- |
|
| 234 |
-func (a *AufsDriver) getParentLayerPaths(id string) ([]string, error) {
|
|
| 235 |
- parentIds, err := getParentIds(a.rootPath(), id) |
|
| 236 |
- if err != nil {
|
|
| 237 |
- return nil, err |
|
| 238 |
- } |
|
| 239 |
- if len(parentIds) == 0 {
|
|
| 240 |
- return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
|
|
| 241 |
- } |
|
| 242 |
- layers := make([]string, len(parentIds)) |
|
| 243 |
- |
|
| 244 |
- // Get the diff paths for all the parent ids |
|
| 245 |
- for i, p := range parentIds {
|
|
| 246 |
- layers[i] = path.Join(a.rootPath(), "diff", p) |
|
| 247 |
- } |
|
| 248 |
- return layers, nil |
|
| 249 |
-} |
|
| 250 |
- |
|
| 251 |
-func (a *AufsDriver) mount(id string) error {
|
|
| 252 |
- // If the id is mounted or we get an error return |
|
| 253 |
- if mounted, err := a.mounted(id); err != nil || mounted {
|
|
| 254 |
- return err |
|
| 255 |
- } |
|
| 256 |
- |
|
| 257 |
- var ( |
|
| 258 |
- target = path.Join(a.rootPath(), "mnt", id) |
|
| 259 |
- rw = path.Join(a.rootPath(), "diff", id) |
|
| 260 |
- ) |
|
| 261 |
- |
|
| 262 |
- layers, err := a.getParentLayerPaths(id) |
|
| 263 |
- if err != nil {
|
|
| 264 |
- return err |
|
| 265 |
- } |
|
| 266 |
- |
|
| 267 |
- if err := a.aufsMount(layers, rw, target); err != nil {
|
|
| 268 |
- return err |
|
| 269 |
- } |
|
| 270 |
- return nil |
|
| 271 |
-} |
|
| 272 |
- |
|
| 273 |
-func (a *AufsDriver) unmount(id string) error {
|
|
| 274 |
- if mounted, err := a.mounted(id); err != nil || !mounted {
|
|
| 275 |
- return err |
|
| 276 |
- } |
|
| 277 |
- target := path.Join(a.rootPath(), "mnt", id) |
|
| 278 |
- return Unmount(target) |
|
| 279 |
-} |
|
| 280 |
- |
|
| 281 |
-func (a *AufsDriver) mounted(id string) (bool, error) {
|
|
| 282 |
- target := path.Join(a.rootPath(), "mnt", id) |
|
| 283 |
- return Mounted(target) |
|
| 284 |
-} |
|
| 285 |
- |
|
| 286 |
-// During cleanup aufs needs to unmount all mountpoints |
|
| 287 |
-func (a *AufsDriver) Cleanup() error {
|
|
| 288 |
- ids, err := loadIds(path.Join(a.rootPath(), "layers")) |
|
| 289 |
- if err != nil {
|
|
| 290 |
- return err |
|
| 291 |
- } |
|
| 292 |
- for _, id := range ids {
|
|
| 293 |
- if err := a.unmount(id); err != nil {
|
|
| 294 |
- return err |
|
| 295 |
- } |
|
| 296 |
- } |
|
| 297 |
- return nil |
|
| 298 |
-} |
|
| 299 |
- |
|
| 300 |
-func (a *AufsDriver) aufsMount(ro []string, rw, target string) error {
|
|
| 301 |
- rwBranch := fmt.Sprintf("%v=rw", rw)
|
|
| 302 |
- roBranches := "" |
|
| 303 |
- for _, layer := range ro {
|
|
| 304 |
- roBranches += fmt.Sprintf("%v=ro+wh:", layer)
|
|
| 305 |
- } |
|
| 306 |
- branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches)
|
|
| 307 |
- |
|
| 308 |
- //if error, try to load aufs kernel module |
|
| 309 |
- if err := mount("none", target, "aufs", 0, branches); err != nil {
|
|
| 310 |
- log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
|
|
| 311 |
- if err := exec.Command("modprobe", "aufs").Run(); err != nil {
|
|
| 312 |
- return fmt.Errorf("Unable to load the AUFS module")
|
|
| 313 |
- } |
|
| 314 |
- log.Printf("...module loaded.")
|
|
| 315 |
- if err := mount("none", target, "aufs", 0, branches); err != nil {
|
|
| 316 |
- return fmt.Errorf("Unable to mount using aufs %s", err)
|
|
| 317 |
- } |
|
| 318 |
- } |
|
| 319 |
- return nil |
|
| 320 |
-} |
| 321 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,517 +0,0 @@ |
| 1 |
-package aufs |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/archive" |
|
| 5 |
- "os" |
|
| 6 |
- "path" |
|
| 7 |
- "testing" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-var ( |
|
| 11 |
- tmp = path.Join(os.TempDir(), "aufs-tests", "aufs") |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-func newDriver(t *testing.T) *AufsDriver {
|
|
| 15 |
- if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 16 |
- t.Fatal(err) |
|
| 17 |
- } |
|
| 18 |
- |
|
| 19 |
- d, err := Init(tmp) |
|
| 20 |
- if err != nil {
|
|
| 21 |
- t.Fatal(err) |
|
| 22 |
- } |
|
| 23 |
- return d.(*AufsDriver) |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-func TestNewAufsDriver(t *testing.T) {
|
|
| 27 |
- if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 28 |
- t.Fatal(err) |
|
| 29 |
- } |
|
| 30 |
- |
|
| 31 |
- d, err := Init(tmp) |
|
| 32 |
- if err != nil {
|
|
| 33 |
- t.Fatal(err) |
|
| 34 |
- } |
|
| 35 |
- defer os.RemoveAll(tmp) |
|
| 36 |
- if d == nil {
|
|
| 37 |
- t.Fatalf("Driver should not be nil")
|
|
| 38 |
- } |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 |
-func TestAufsString(t *testing.T) {
|
|
| 42 |
- d := newDriver(t) |
|
| 43 |
- defer os.RemoveAll(tmp) |
|
| 44 |
- |
|
| 45 |
- if d.String() != "aufs" {
|
|
| 46 |
- t.Fatalf("Expected aufs got %s", d.String())
|
|
| 47 |
- } |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func TestCreateDirStructure(t *testing.T) {
|
|
| 51 |
- newDriver(t) |
|
| 52 |
- defer os.RemoveAll(tmp) |
|
| 53 |
- |
|
| 54 |
- paths := []string{
|
|
| 55 |
- "mnt", |
|
| 56 |
- "layers", |
|
| 57 |
- "diff", |
|
| 58 |
- } |
|
| 59 |
- |
|
| 60 |
- for _, p := range paths {
|
|
| 61 |
- if _, err := os.Stat(path.Join(tmp, p)); err != nil {
|
|
| 62 |
- t.Fatal(err) |
|
| 63 |
- } |
|
| 64 |
- } |
|
| 65 |
-} |
|
| 66 |
- |
|
| 67 |
-// We should be able to create two drivers with the same dir structure |
|
| 68 |
-func TestNewDriverFromExistingDir(t *testing.T) {
|
|
| 69 |
- if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 70 |
- t.Fatal(err) |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- if _, err := Init(tmp); err != nil {
|
|
| 74 |
- t.Fatal(err) |
|
| 75 |
- } |
|
| 76 |
- if _, err := Init(tmp); err != nil {
|
|
| 77 |
- t.Fatal(err) |
|
| 78 |
- } |
|
| 79 |
- os.RemoveAll(tmp) |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func TestCreateNewDir(t *testing.T) {
|
|
| 83 |
- d := newDriver(t) |
|
| 84 |
- defer os.RemoveAll(tmp) |
|
| 85 |
- |
|
| 86 |
- if err := d.Create("1", ""); err != nil {
|
|
| 87 |
- t.Fatal(err) |
|
| 88 |
- } |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func TestCreateNewDirStructure(t *testing.T) {
|
|
| 92 |
- d := newDriver(t) |
|
| 93 |
- defer os.RemoveAll(tmp) |
|
| 94 |
- |
|
| 95 |
- if err := d.Create("1", ""); err != nil {
|
|
| 96 |
- t.Fatal(err) |
|
| 97 |
- } |
|
| 98 |
- |
|
| 99 |
- paths := []string{
|
|
| 100 |
- "mnt", |
|
| 101 |
- "diff", |
|
| 102 |
- "layers", |
|
| 103 |
- } |
|
| 104 |
- |
|
| 105 |
- for _, p := range paths {
|
|
| 106 |
- if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
|
|
| 107 |
- t.Fatal(err) |
|
| 108 |
- } |
|
| 109 |
- } |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-func TestRemoveImage(t *testing.T) {
|
|
| 113 |
- d := newDriver(t) |
|
| 114 |
- defer os.RemoveAll(tmp) |
|
| 115 |
- |
|
| 116 |
- if err := d.Create("1", ""); err != nil {
|
|
| 117 |
- t.Fatal(err) |
|
| 118 |
- } |
|
| 119 |
- |
|
| 120 |
- if err := d.Remove("1"); err != nil {
|
|
| 121 |
- t.Fatal(err) |
|
| 122 |
- } |
|
| 123 |
- |
|
| 124 |
- paths := []string{
|
|
| 125 |
- "mnt", |
|
| 126 |
- "diff", |
|
| 127 |
- "layers", |
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- for _, p := range paths {
|
|
| 131 |
- if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
|
|
| 132 |
- t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
|
|
| 133 |
- } |
|
| 134 |
- } |
|
| 135 |
-} |
|
| 136 |
- |
|
| 137 |
-func TestGetWithoutParent(t *testing.T) {
|
|
| 138 |
- d := newDriver(t) |
|
| 139 |
- defer os.RemoveAll(tmp) |
|
| 140 |
- |
|
| 141 |
- if err := d.Create("1", ""); err != nil {
|
|
| 142 |
- t.Fatal(err) |
|
| 143 |
- } |
|
| 144 |
- |
|
| 145 |
- diffPath, err := d.Get("1")
|
|
| 146 |
- if err != nil {
|
|
| 147 |
- t.Fatal(err) |
|
| 148 |
- } |
|
| 149 |
- expected := path.Join(tmp, "diff", "1") |
|
| 150 |
- if diffPath != expected {
|
|
| 151 |
- t.Fatalf("Expected path %s got %s", expected, diffPath)
|
|
| 152 |
- } |
|
| 153 |
-} |
|
| 154 |
- |
|
| 155 |
-func TestCleanupWithNoDirs(t *testing.T) {
|
|
| 156 |
- d := newDriver(t) |
|
| 157 |
- defer os.RemoveAll(tmp) |
|
| 158 |
- |
|
| 159 |
- if err := d.Cleanup(); err != nil {
|
|
| 160 |
- t.Fatal(err) |
|
| 161 |
- } |
|
| 162 |
-} |
|
| 163 |
- |
|
| 164 |
-func TestCleanupWithDir(t *testing.T) {
|
|
| 165 |
- d := newDriver(t) |
|
| 166 |
- defer os.RemoveAll(tmp) |
|
| 167 |
- |
|
| 168 |
- if err := d.Create("1", ""); err != nil {
|
|
| 169 |
- t.Fatal(err) |
|
| 170 |
- } |
|
| 171 |
- |
|
| 172 |
- if err := d.Cleanup(); err != nil {
|
|
| 173 |
- t.Fatal(err) |
|
| 174 |
- } |
|
| 175 |
-} |
|
| 176 |
- |
|
| 177 |
-func TestMountedFalseResponse(t *testing.T) {
|
|
| 178 |
- d := newDriver(t) |
|
| 179 |
- defer os.RemoveAll(tmp) |
|
| 180 |
- |
|
| 181 |
- if err := d.Create("1", ""); err != nil {
|
|
| 182 |
- t.Fatal(err) |
|
| 183 |
- } |
|
| 184 |
- |
|
| 185 |
- response, err := d.mounted("1")
|
|
| 186 |
- if err != nil {
|
|
| 187 |
- t.Fatal(err) |
|
| 188 |
- } |
|
| 189 |
- |
|
| 190 |
- if response != false {
|
|
| 191 |
- t.Fatalf("Response if dir id 1 is mounted should be false")
|
|
| 192 |
- } |
|
| 193 |
-} |
|
| 194 |
- |
|
| 195 |
-func TestMountedTrueReponse(t *testing.T) {
|
|
| 196 |
- d := newDriver(t) |
|
| 197 |
- defer os.RemoveAll(tmp) |
|
| 198 |
- defer d.Cleanup() |
|
| 199 |
- |
|
| 200 |
- if err := d.Create("1", ""); err != nil {
|
|
| 201 |
- t.Fatal(err) |
|
| 202 |
- } |
|
| 203 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 204 |
- t.Fatal(err) |
|
| 205 |
- } |
|
| 206 |
- |
|
| 207 |
- _, err := d.Get("2")
|
|
| 208 |
- if err != nil {
|
|
| 209 |
- t.Fatal(err) |
|
| 210 |
- } |
|
| 211 |
- |
|
| 212 |
- response, err := d.mounted("2")
|
|
| 213 |
- if err != nil {
|
|
| 214 |
- t.Fatal(err) |
|
| 215 |
- } |
|
| 216 |
- |
|
| 217 |
- if response != true {
|
|
| 218 |
- t.Fatalf("Response if dir id 2 is mounted should be true")
|
|
| 219 |
- } |
|
| 220 |
-} |
|
| 221 |
- |
|
| 222 |
-func TestMountWithParent(t *testing.T) {
|
|
| 223 |
- d := newDriver(t) |
|
| 224 |
- defer os.RemoveAll(tmp) |
|
| 225 |
- |
|
| 226 |
- if err := d.Create("1", ""); err != nil {
|
|
| 227 |
- t.Fatal(err) |
|
| 228 |
- } |
|
| 229 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 230 |
- t.Fatal(err) |
|
| 231 |
- } |
|
| 232 |
- |
|
| 233 |
- defer func() {
|
|
| 234 |
- if err := d.Cleanup(); err != nil {
|
|
| 235 |
- t.Fatal(err) |
|
| 236 |
- } |
|
| 237 |
- }() |
|
| 238 |
- |
|
| 239 |
- mntPath, err := d.Get("2")
|
|
| 240 |
- if err != nil {
|
|
| 241 |
- t.Fatal(err) |
|
| 242 |
- } |
|
| 243 |
- if mntPath == "" {
|
|
| 244 |
- t.Fatal("mntPath should not be empty string")
|
|
| 245 |
- } |
|
| 246 |
- |
|
| 247 |
- expected := path.Join(tmp, "mnt", "2") |
|
| 248 |
- if mntPath != expected {
|
|
| 249 |
- t.Fatalf("Expected %s got %s", expected, mntPath)
|
|
| 250 |
- } |
|
| 251 |
-} |
|
| 252 |
- |
|
| 253 |
-func TestRemoveMountedDir(t *testing.T) {
|
|
| 254 |
- d := newDriver(t) |
|
| 255 |
- defer os.RemoveAll(tmp) |
|
| 256 |
- |
|
| 257 |
- if err := d.Create("1", ""); err != nil {
|
|
| 258 |
- t.Fatal(err) |
|
| 259 |
- } |
|
| 260 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 261 |
- t.Fatal(err) |
|
| 262 |
- } |
|
| 263 |
- |
|
| 264 |
- defer func() {
|
|
| 265 |
- if err := d.Cleanup(); err != nil {
|
|
| 266 |
- t.Fatal(err) |
|
| 267 |
- } |
|
| 268 |
- }() |
|
| 269 |
- |
|
| 270 |
- mntPath, err := d.Get("2")
|
|
| 271 |
- if err != nil {
|
|
| 272 |
- t.Fatal(err) |
|
| 273 |
- } |
|
| 274 |
- if mntPath == "" {
|
|
| 275 |
- t.Fatal("mntPath should not be empty string")
|
|
| 276 |
- } |
|
| 277 |
- |
|
| 278 |
- mounted, err := d.mounted("2")
|
|
| 279 |
- if err != nil {
|
|
| 280 |
- t.Fatal(err) |
|
| 281 |
- } |
|
| 282 |
- |
|
| 283 |
- if !mounted {
|
|
| 284 |
- t.Fatalf("Dir id 2 should be mounted")
|
|
| 285 |
- } |
|
| 286 |
- |
|
| 287 |
- if err := d.Remove("2"); err != nil {
|
|
| 288 |
- t.Fatal(err) |
|
| 289 |
- } |
|
| 290 |
-} |
|
| 291 |
- |
|
| 292 |
-func TestCreateWithInvalidParent(t *testing.T) {
|
|
| 293 |
- d := newDriver(t) |
|
| 294 |
- defer os.RemoveAll(tmp) |
|
| 295 |
- |
|
| 296 |
- if err := d.Create("1", "docker"); err == nil {
|
|
| 297 |
- t.Fatalf("Error should not be nil with parent does not exist")
|
|
| 298 |
- } |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 |
-func TestGetDiff(t *testing.T) {
|
|
| 302 |
- d := newDriver(t) |
|
| 303 |
- defer os.RemoveAll(tmp) |
|
| 304 |
- |
|
| 305 |
- if err := d.Create("1", ""); err != nil {
|
|
| 306 |
- t.Fatal(err) |
|
| 307 |
- } |
|
| 308 |
- |
|
| 309 |
- diffPath, err := d.Get("1")
|
|
| 310 |
- if err != nil {
|
|
| 311 |
- t.Fatal(err) |
|
| 312 |
- } |
|
| 313 |
- |
|
| 314 |
- // Add a file to the diff path with a fixed size |
|
| 315 |
- size := int64(1024) |
|
| 316 |
- |
|
| 317 |
- f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 318 |
- if err != nil {
|
|
| 319 |
- t.Fatal(err) |
|
| 320 |
- } |
|
| 321 |
- if err := f.Truncate(size); err != nil {
|
|
| 322 |
- t.Fatal(err) |
|
| 323 |
- } |
|
| 324 |
- f.Close() |
|
| 325 |
- |
|
| 326 |
- a, err := d.Diff("1")
|
|
| 327 |
- if err != nil {
|
|
| 328 |
- t.Fatal(err) |
|
| 329 |
- } |
|
| 330 |
- if a == nil {
|
|
| 331 |
- t.Fatalf("Archive should not be nil")
|
|
| 332 |
- } |
|
| 333 |
-} |
|
| 334 |
- |
|
| 335 |
-func TestChanges(t *testing.T) {
|
|
| 336 |
- d := newDriver(t) |
|
| 337 |
- defer os.RemoveAll(tmp) |
|
| 338 |
- |
|
| 339 |
- if err := d.Create("1", ""); err != nil {
|
|
| 340 |
- t.Fatal(err) |
|
| 341 |
- } |
|
| 342 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 343 |
- t.Fatal(err) |
|
| 344 |
- } |
|
| 345 |
- |
|
| 346 |
- defer func() {
|
|
| 347 |
- if err := d.Cleanup(); err != nil {
|
|
| 348 |
- t.Fatal(err) |
|
| 349 |
- } |
|
| 350 |
- }() |
|
| 351 |
- |
|
| 352 |
- mntPoint, err := d.Get("2")
|
|
| 353 |
- if err != nil {
|
|
| 354 |
- t.Fatal(err) |
|
| 355 |
- } |
|
| 356 |
- |
|
| 357 |
- // Create a file to save in the mountpoint |
|
| 358 |
- f, err := os.Create(path.Join(mntPoint, "test.txt")) |
|
| 359 |
- if err != nil {
|
|
| 360 |
- t.Fatal(err) |
|
| 361 |
- } |
|
| 362 |
- |
|
| 363 |
- if _, err := f.WriteString("testline"); err != nil {
|
|
| 364 |
- t.Fatal(err) |
|
| 365 |
- } |
|
| 366 |
- if err := f.Close(); err != nil {
|
|
| 367 |
- t.Fatal(err) |
|
| 368 |
- } |
|
| 369 |
- |
|
| 370 |
- changes, err := d.Changes("2")
|
|
| 371 |
- if err != nil {
|
|
| 372 |
- t.Fatal(err) |
|
| 373 |
- } |
|
| 374 |
- if len(changes) != 1 {
|
|
| 375 |
- t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 376 |
- } |
|
| 377 |
- change := changes[0] |
|
| 378 |
- |
|
| 379 |
- expectedPath := "/test.txt" |
|
| 380 |
- if change.Path != expectedPath {
|
|
| 381 |
- t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 382 |
- } |
|
| 383 |
- |
|
| 384 |
- if change.Kind != archive.ChangeAdd {
|
|
| 385 |
- t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 386 |
- } |
|
| 387 |
- |
|
| 388 |
- if err := d.Create("3", "2"); err != nil {
|
|
| 389 |
- t.Fatal(err) |
|
| 390 |
- } |
|
| 391 |
- mntPoint, err = d.Get("3")
|
|
| 392 |
- if err != nil {
|
|
| 393 |
- t.Fatal(err) |
|
| 394 |
- } |
|
| 395 |
- |
|
| 396 |
- // Create a file to save in the mountpoint |
|
| 397 |
- f, err = os.Create(path.Join(mntPoint, "test2.txt")) |
|
| 398 |
- if err != nil {
|
|
| 399 |
- t.Fatal(err) |
|
| 400 |
- } |
|
| 401 |
- |
|
| 402 |
- if _, err := f.WriteString("testline"); err != nil {
|
|
| 403 |
- t.Fatal(err) |
|
| 404 |
- } |
|
| 405 |
- if err := f.Close(); err != nil {
|
|
| 406 |
- t.Fatal(err) |
|
| 407 |
- } |
|
| 408 |
- |
|
| 409 |
- changes, err = d.Changes("3")
|
|
| 410 |
- if err != nil {
|
|
| 411 |
- t.Fatal(err) |
|
| 412 |
- } |
|
| 413 |
- |
|
| 414 |
- if len(changes) != 1 {
|
|
| 415 |
- t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 416 |
- } |
|
| 417 |
- change = changes[0] |
|
| 418 |
- |
|
| 419 |
- expectedPath = "/test2.txt" |
|
| 420 |
- if change.Path != expectedPath {
|
|
| 421 |
- t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 422 |
- } |
|
| 423 |
- |
|
| 424 |
- if change.Kind != archive.ChangeAdd {
|
|
| 425 |
- t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 426 |
- } |
|
| 427 |
-} |
|
| 428 |
- |
|
| 429 |
-func TestDiffSize(t *testing.T) {
|
|
| 430 |
- d := newDriver(t) |
|
| 431 |
- defer os.RemoveAll(tmp) |
|
| 432 |
- |
|
| 433 |
- if err := d.Create("1", ""); err != nil {
|
|
| 434 |
- t.Fatal(err) |
|
| 435 |
- } |
|
| 436 |
- |
|
| 437 |
- diffPath, err := d.Get("1")
|
|
| 438 |
- if err != nil {
|
|
| 439 |
- t.Fatal(err) |
|
| 440 |
- } |
|
| 441 |
- |
|
| 442 |
- // Add a file to the diff path with a fixed size |
|
| 443 |
- size := int64(1024) |
|
| 444 |
- |
|
| 445 |
- f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 446 |
- if err != nil {
|
|
| 447 |
- t.Fatal(err) |
|
| 448 |
- } |
|
| 449 |
- f.Truncate(size) |
|
| 450 |
- s, err := f.Stat() |
|
| 451 |
- if err != nil {
|
|
| 452 |
- t.Fatal(err) |
|
| 453 |
- } |
|
| 454 |
- size = s.Size() |
|
| 455 |
- if err := f.Close(); err != nil {
|
|
| 456 |
- t.Fatal(err) |
|
| 457 |
- } |
|
| 458 |
- |
|
| 459 |
- diffSize, err := d.Size("1")
|
|
| 460 |
- if err != nil {
|
|
| 461 |
- t.Fatal(err) |
|
| 462 |
- } |
|
| 463 |
- if diffSize != size {
|
|
| 464 |
- t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
| 465 |
- } |
|
| 466 |
-} |
|
| 467 |
- |
|
| 468 |
-func TestApplyDiff(t *testing.T) {
|
|
| 469 |
- d := newDriver(t) |
|
| 470 |
- defer os.RemoveAll(tmp) |
|
| 471 |
- defer d.Cleanup() |
|
| 472 |
- |
|
| 473 |
- if err := d.Create("1", ""); err != nil {
|
|
| 474 |
- t.Fatal(err) |
|
| 475 |
- } |
|
| 476 |
- |
|
| 477 |
- diffPath, err := d.Get("1")
|
|
| 478 |
- if err != nil {
|
|
| 479 |
- t.Fatal(err) |
|
| 480 |
- } |
|
| 481 |
- |
|
| 482 |
- // Add a file to the diff path with a fixed size |
|
| 483 |
- size := int64(1024) |
|
| 484 |
- |
|
| 485 |
- f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 486 |
- if err != nil {
|
|
| 487 |
- t.Fatal(err) |
|
| 488 |
- } |
|
| 489 |
- f.Truncate(size) |
|
| 490 |
- f.Close() |
|
| 491 |
- |
|
| 492 |
- diff, err := d.Diff("1")
|
|
| 493 |
- if err != nil {
|
|
| 494 |
- t.Fatal(err) |
|
| 495 |
- } |
|
| 496 |
- |
|
| 497 |
- if err := d.Create("2", ""); err != nil {
|
|
| 498 |
- t.Fatal(err) |
|
| 499 |
- } |
|
| 500 |
- if err := d.Create("3", "2"); err != nil {
|
|
| 501 |
- t.Fatal(err) |
|
| 502 |
- } |
|
| 503 |
- |
|
| 504 |
- if err := d.ApplyDiff("3", diff); err != nil {
|
|
| 505 |
- t.Fatal(err) |
|
| 506 |
- } |
|
| 507 |
- |
|
| 508 |
- // Ensure that the file is in the mount point for id 3 |
|
| 509 |
- |
|
| 510 |
- mountPoint, err := d.Get("3")
|
|
| 511 |
- if err != nil {
|
|
| 512 |
- t.Fatal(err) |
|
| 513 |
- } |
|
| 514 |
- if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
|
|
| 515 |
- t.Fatal(err) |
|
| 516 |
- } |
|
| 517 |
-} |
| 518 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,46 +0,0 @@ |
| 1 |
-package aufs |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "io/ioutil" |
|
| 6 |
- "os" |
|
| 7 |
- "path" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-// Return all the directories |
|
| 11 |
-func loadIds(root string) ([]string, error) {
|
|
| 12 |
- dirs, err := ioutil.ReadDir(root) |
|
| 13 |
- if err != nil {
|
|
| 14 |
- return nil, err |
|
| 15 |
- } |
|
| 16 |
- out := []string{}
|
|
| 17 |
- for _, d := range dirs {
|
|
| 18 |
- if !d.IsDir() {
|
|
| 19 |
- out = append(out, d.Name()) |
|
| 20 |
- } |
|
| 21 |
- } |
|
| 22 |
- return out, nil |
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 |
-// Read the layers file for the current id and return all the |
|
| 26 |
-// layers represented by new lines in the file |
|
| 27 |
-// |
|
| 28 |
-// If there are no lines in the file then the id has no parent |
|
| 29 |
-// and an empty slice is returned. |
|
| 30 |
-func getParentIds(root, id string) ([]string, error) {
|
|
| 31 |
- f, err := os.Open(path.Join(root, "layers", id)) |
|
| 32 |
- if err != nil {
|
|
| 33 |
- return nil, err |
|
| 34 |
- } |
|
| 35 |
- defer f.Close() |
|
| 36 |
- |
|
| 37 |
- out := []string{}
|
|
| 38 |
- s := bufio.NewScanner(f) |
|
| 39 |
- |
|
| 40 |
- for s.Scan() {
|
|
| 41 |
- if t := s.Text(); t != "" {
|
|
| 42 |
- out = append(out, s.Text()) |
|
| 43 |
- } |
|
| 44 |
- } |
|
| 45 |
- return out, s.Err() |
|
| 46 |
-} |
| 47 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,37 +0,0 @@ |
| 1 |
-package aufs |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/utils" |
|
| 5 |
- "os" |
|
| 6 |
- "os/exec" |
|
| 7 |
- "path/filepath" |
|
| 8 |
- "syscall" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-func Unmount(target string) error {
|
|
| 12 |
- if err := exec.Command("auplink", target, "flush").Run(); err != nil {
|
|
| 13 |
- utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
|
|
| 14 |
- } |
|
| 15 |
- if err := syscall.Unmount(target, 0); err != nil {
|
|
| 16 |
- return err |
|
| 17 |
- } |
|
| 18 |
- return nil |
|
| 19 |
-} |
|
| 20 |
- |
|
| 21 |
-func Mounted(mountpoint string) (bool, error) {
|
|
| 22 |
- mntpoint, err := os.Stat(mountpoint) |
|
| 23 |
- if err != nil {
|
|
| 24 |
- if os.IsNotExist(err) {
|
|
| 25 |
- return false, nil |
|
| 26 |
- } |
|
| 27 |
- return false, err |
|
| 28 |
- } |
|
| 29 |
- parent, err := os.Stat(filepath.Join(mountpoint, "..")) |
|
| 30 |
- if err != nil {
|
|
| 31 |
- return false, err |
|
| 32 |
- } |
|
| 33 |
- mntpointSt := mntpoint.Sys().(*syscall.Stat_t) |
|
| 34 |
- parentSt := parent.Sys().(*syscall.Stat_t) |
|
| 35 |
- |
|
| 36 |
- return mntpointSt.Dev != parentSt.Dev, nil |
|
| 37 |
-} |
| 8 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,803 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/dotcloud/docker/utils" |
|
| 7 |
- "io" |
|
| 8 |
- "io/ioutil" |
|
| 9 |
- "os" |
|
| 10 |
- "os/exec" |
|
| 11 |
- "path" |
|
| 12 |
- "path/filepath" |
|
| 13 |
- "strconv" |
|
| 14 |
- "sync" |
|
| 15 |
- "syscall" |
|
| 16 |
- "time" |
|
| 17 |
-) |
|
| 18 |
- |
|
| 19 |
-var ( |
|
| 20 |
- DefaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024 |
|
| 21 |
- DefaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024 |
|
| 22 |
- DefaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024 |
|
| 23 |
-) |
|
| 24 |
- |
|
| 25 |
-type DevInfo struct {
|
|
| 26 |
- Hash string `json:"-"` |
|
| 27 |
- DeviceId int `json:"device_id"` |
|
| 28 |
- Size uint64 `json:"size"` |
|
| 29 |
- TransactionId uint64 `json:"transaction_id"` |
|
| 30 |
- Initialized bool `json:"initialized"` |
|
| 31 |
- devices *DeviceSet `json:"-"` |
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-type MetaData struct {
|
|
| 35 |
- Devices map[string]*DevInfo `json:devices` |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-type DeviceSet struct {
|
|
| 39 |
- MetaData |
|
| 40 |
- sync.Mutex |
|
| 41 |
- root string |
|
| 42 |
- devicePrefix string |
|
| 43 |
- TransactionId uint64 |
|
| 44 |
- NewTransactionId uint64 |
|
| 45 |
- nextFreeDevice int |
|
| 46 |
- activeMounts map[string]int |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-type DiskUsage struct {
|
|
| 50 |
- Used uint64 |
|
| 51 |
- Total uint64 |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-type Status struct {
|
|
| 55 |
- PoolName string |
|
| 56 |
- DataLoopback string |
|
| 57 |
- MetadataLoopback string |
|
| 58 |
- Data DiskUsage |
|
| 59 |
- Metadata DiskUsage |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-func getDevName(name string) string {
|
|
| 63 |
- return "/dev/mapper/" + name |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-func (info *DevInfo) Name() string {
|
|
| 67 |
- hash := info.Hash |
|
| 68 |
- if hash == "" {
|
|
| 69 |
- hash = "base" |
|
| 70 |
- } |
|
| 71 |
- return fmt.Sprintf("%s-%s", info.devices.devicePrefix, hash)
|
|
| 72 |
-} |
|
| 73 |
- |
|
| 74 |
-func (info *DevInfo) DevName() string {
|
|
| 75 |
- return getDevName(info.Name()) |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-func (devices *DeviceSet) loopbackDir() string {
|
|
| 79 |
- return path.Join(devices.root, "devicemapper") |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func (devices *DeviceSet) jsonFile() string {
|
|
| 83 |
- return path.Join(devices.loopbackDir(), "json") |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-func (devices *DeviceSet) getPoolName() string {
|
|
| 87 |
- return devices.devicePrefix + "-pool" |
|
| 88 |
-} |
|
| 89 |
- |
|
| 90 |
-func (devices *DeviceSet) getPoolDevName() string {
|
|
| 91 |
- return getDevName(devices.getPoolName()) |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-func (devices *DeviceSet) hasImage(name string) bool {
|
|
| 95 |
- dirname := devices.loopbackDir() |
|
| 96 |
- filename := path.Join(dirname, name) |
|
| 97 |
- |
|
| 98 |
- _, err := os.Stat(filename) |
|
| 99 |
- return err == nil |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-// ensureImage creates a sparse file of <size> bytes at the path |
|
| 103 |
-// <root>/devicemapper/<name>. |
|
| 104 |
-// If the file already exists, it does nothing. |
|
| 105 |
-// Either way it returns the full path. |
|
| 106 |
-func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
|
| 107 |
- dirname := devices.loopbackDir() |
|
| 108 |
- filename := path.Join(dirname, name) |
|
| 109 |
- |
|
| 110 |
- if err := os.MkdirAll(dirname, 0700); err != nil && !os.IsExist(err) {
|
|
| 111 |
- return "", err |
|
| 112 |
- } |
|
| 113 |
- |
|
| 114 |
- if _, err := os.Stat(filename); err != nil {
|
|
| 115 |
- if !os.IsNotExist(err) {
|
|
| 116 |
- return "", err |
|
| 117 |
- } |
|
| 118 |
- utils.Debugf("Creating loopback file %s for device-manage use", filename)
|
|
| 119 |
- file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) |
|
| 120 |
- if err != nil {
|
|
| 121 |
- return "", err |
|
| 122 |
- } |
|
| 123 |
- defer file.Close() |
|
| 124 |
- |
|
| 125 |
- if err = file.Truncate(size); err != nil {
|
|
| 126 |
- return "", err |
|
| 127 |
- } |
|
| 128 |
- } |
|
| 129 |
- return filename, nil |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func (devices *DeviceSet) allocateDeviceId() int {
|
|
| 133 |
- // TODO: Add smarter reuse of deleted devices |
|
| 134 |
- id := devices.nextFreeDevice |
|
| 135 |
- devices.nextFreeDevice = devices.nextFreeDevice + 1 |
|
| 136 |
- return id |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-func (devices *DeviceSet) allocateTransactionId() uint64 {
|
|
| 140 |
- devices.NewTransactionId = devices.NewTransactionId + 1 |
|
| 141 |
- return devices.NewTransactionId |
|
| 142 |
-} |
|
| 143 |
- |
|
| 144 |
-func (devices *DeviceSet) saveMetadata() error {
|
|
| 145 |
- jsonData, err := json.Marshal(devices.MetaData) |
|
| 146 |
- if err != nil {
|
|
| 147 |
- return fmt.Errorf("Error encoding metaadata to json: %s", err)
|
|
| 148 |
- } |
|
| 149 |
- tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json") |
|
| 150 |
- if err != nil {
|
|
| 151 |
- return fmt.Errorf("Error creating metadata file: %s", err)
|
|
| 152 |
- } |
|
| 153 |
- |
|
| 154 |
- n, err := tmpFile.Write(jsonData) |
|
| 155 |
- if err != nil {
|
|
| 156 |
- return fmt.Errorf("Error writing metadata to %s: %s", tmpFile.Name(), err)
|
|
| 157 |
- } |
|
| 158 |
- if n < len(jsonData) {
|
|
| 159 |
- return io.ErrShortWrite |
|
| 160 |
- } |
|
| 161 |
- if err := tmpFile.Sync(); err != nil {
|
|
| 162 |
- return fmt.Errorf("Error syncing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 163 |
- } |
|
| 164 |
- if err := tmpFile.Close(); err != nil {
|
|
| 165 |
- return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 166 |
- } |
|
| 167 |
- if err := os.Rename(tmpFile.Name(), devices.jsonFile()); err != nil {
|
|
| 168 |
- return fmt.Errorf("Error committing metadata file", err)
|
|
| 169 |
- } |
|
| 170 |
- |
|
| 171 |
- if devices.NewTransactionId != devices.TransactionId {
|
|
| 172 |
- if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
|
|
| 173 |
- return fmt.Errorf("Error setting devmapper transition ID: %s", err)
|
|
| 174 |
- } |
|
| 175 |
- devices.TransactionId = devices.NewTransactionId |
|
| 176 |
- } |
|
| 177 |
- return nil |
|
| 178 |
-} |
|
| 179 |
- |
|
| 180 |
-func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) {
|
|
| 181 |
- utils.Debugf("registerDevice(%v, %v)", id, hash)
|
|
| 182 |
- info := &DevInfo{
|
|
| 183 |
- Hash: hash, |
|
| 184 |
- DeviceId: id, |
|
| 185 |
- Size: size, |
|
| 186 |
- TransactionId: devices.allocateTransactionId(), |
|
| 187 |
- Initialized: false, |
|
| 188 |
- devices: devices, |
|
| 189 |
- } |
|
| 190 |
- |
|
| 191 |
- devices.Devices[hash] = info |
|
| 192 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 193 |
- // Try to remove unused device |
|
| 194 |
- delete(devices.Devices, hash) |
|
| 195 |
- return nil, err |
|
| 196 |
- } |
|
| 197 |
- |
|
| 198 |
- return info, nil |
|
| 199 |
-} |
|
| 200 |
- |
|
| 201 |
-func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error {
|
|
| 202 |
- utils.Debugf("activateDeviceIfNeeded(%v)", hash)
|
|
| 203 |
- info := devices.Devices[hash] |
|
| 204 |
- if info == nil {
|
|
| 205 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 206 |
- } |
|
| 207 |
- |
|
| 208 |
- if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
|
|
| 209 |
- return nil |
|
| 210 |
- } |
|
| 211 |
- |
|
| 212 |
- return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size) |
|
| 213 |
-} |
|
| 214 |
- |
|
| 215 |
-func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
|
|
| 216 |
- devname := info.DevName() |
|
| 217 |
- |
|
| 218 |
- err := exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname).Run()
|
|
| 219 |
- if err != nil {
|
|
| 220 |
- err = exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname).Run()
|
|
| 221 |
- } |
|
| 222 |
- if err != nil {
|
|
| 223 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 224 |
- return err |
|
| 225 |
- } |
|
| 226 |
- return nil |
|
| 227 |
-} |
|
| 228 |
- |
|
| 229 |
-func (devices *DeviceSet) loadMetaData() error {
|
|
| 230 |
- utils.Debugf("loadMetadata()")
|
|
| 231 |
- defer utils.Debugf("loadMetadata END")
|
|
| 232 |
- _, _, _, params, err := getStatus(devices.getPoolName()) |
|
| 233 |
- if err != nil {
|
|
| 234 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 235 |
- return err |
|
| 236 |
- } |
|
| 237 |
- |
|
| 238 |
- if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
|
|
| 239 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 240 |
- return err |
|
| 241 |
- } |
|
| 242 |
- devices.NewTransactionId = devices.TransactionId |
|
| 243 |
- |
|
| 244 |
- jsonData, err := ioutil.ReadFile(devices.jsonFile()) |
|
| 245 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 246 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 247 |
- return err |
|
| 248 |
- } |
|
| 249 |
- |
|
| 250 |
- devices.MetaData.Devices = make(map[string]*DevInfo) |
|
| 251 |
- if jsonData != nil {
|
|
| 252 |
- if err := json.Unmarshal(jsonData, &devices.MetaData); err != nil {
|
|
| 253 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 254 |
- return err |
|
| 255 |
- } |
|
| 256 |
- } |
|
| 257 |
- |
|
| 258 |
- for hash, d := range devices.Devices {
|
|
| 259 |
- d.Hash = hash |
|
| 260 |
- d.devices = devices |
|
| 261 |
- |
|
| 262 |
- if d.DeviceId >= devices.nextFreeDevice {
|
|
| 263 |
- devices.nextFreeDevice = d.DeviceId + 1 |
|
| 264 |
- } |
|
| 265 |
- |
|
| 266 |
- // If the transaction id is larger than the actual one we lost the device due to some crash |
|
| 267 |
- if d.TransactionId > devices.TransactionId {
|
|
| 268 |
- utils.Debugf("Removing lost device %s with id %d", hash, d.TransactionId)
|
|
| 269 |
- delete(devices.Devices, hash) |
|
| 270 |
- } |
|
| 271 |
- } |
|
| 272 |
- return nil |
|
| 273 |
-} |
|
| 274 |
- |
|
| 275 |
-func (devices *DeviceSet) setupBaseImage() error {
|
|
| 276 |
- oldInfo := devices.Devices[""] |
|
| 277 |
- if oldInfo != nil && oldInfo.Initialized {
|
|
| 278 |
- return nil |
|
| 279 |
- } |
|
| 280 |
- |
|
| 281 |
- if oldInfo != nil && !oldInfo.Initialized {
|
|
| 282 |
- utils.Debugf("Removing uninitialized base image")
|
|
| 283 |
- if err := devices.removeDevice(""); err != nil {
|
|
| 284 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 285 |
- return err |
|
| 286 |
- } |
|
| 287 |
- } |
|
| 288 |
- |
|
| 289 |
- utils.Debugf("Initializing base device-manager snapshot")
|
|
| 290 |
- |
|
| 291 |
- id := devices.allocateDeviceId() |
|
| 292 |
- |
|
| 293 |
- // Create initial device |
|
| 294 |
- if err := createDevice(devices.getPoolDevName(), id); err != nil {
|
|
| 295 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 296 |
- return err |
|
| 297 |
- } |
|
| 298 |
- |
|
| 299 |
- utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
|
|
| 300 |
- info, err := devices.registerDevice(id, "", DefaultBaseFsSize) |
|
| 301 |
- if err != nil {
|
|
| 302 |
- _ = deleteDevice(devices.getPoolDevName(), id) |
|
| 303 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 304 |
- return err |
|
| 305 |
- } |
|
| 306 |
- |
|
| 307 |
- utils.Debugf("Creating filesystem on base device-manager snapshot")
|
|
| 308 |
- |
|
| 309 |
- if err = devices.activateDeviceIfNeeded(""); err != nil {
|
|
| 310 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 311 |
- return err |
|
| 312 |
- } |
|
| 313 |
- |
|
| 314 |
- if err := devices.createFilesystem(info); err != nil {
|
|
| 315 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 316 |
- return err |
|
| 317 |
- } |
|
| 318 |
- |
|
| 319 |
- info.Initialized = true |
|
| 320 |
- if err = devices.saveMetadata(); err != nil {
|
|
| 321 |
- info.Initialized = false |
|
| 322 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 323 |
- return err |
|
| 324 |
- } |
|
| 325 |
- |
|
| 326 |
- return nil |
|
| 327 |
-} |
|
| 328 |
- |
|
| 329 |
-func setCloseOnExec(name string) {
|
|
| 330 |
- fileInfos, _ := ioutil.ReadDir("/proc/self/fd")
|
|
| 331 |
- if fileInfos != nil {
|
|
| 332 |
- for _, i := range fileInfos {
|
|
| 333 |
- link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name()))
|
|
| 334 |
- if link == name {
|
|
| 335 |
- fd, err := strconv.Atoi(i.Name()) |
|
| 336 |
- if err == nil {
|
|
| 337 |
- syscall.CloseOnExec(fd) |
|
| 338 |
- } |
|
| 339 |
- } |
|
| 340 |
- } |
|
| 341 |
- } |
|
| 342 |
-} |
|
| 343 |
- |
|
| 344 |
-func (devices *DeviceSet) log(level int, file string, line int, dmError int, message string) {
|
|
| 345 |
- if level >= 7 {
|
|
| 346 |
- return // Ignore _LOG_DEBUG |
|
| 347 |
- } |
|
| 348 |
- |
|
| 349 |
- utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 350 |
-} |
|
| 351 |
- |
|
| 352 |
-func major(device uint64) uint64 {
|
|
| 353 |
- return (device >> 8) & 0xfff |
|
| 354 |
-} |
|
| 355 |
- |
|
| 356 |
-func minor(device uint64) uint64 {
|
|
| 357 |
- return (device & 0xff) | ((device >> 12) & 0xfff00) |
|
| 358 |
-} |
|
| 359 |
- |
|
| 360 |
-func (devices *DeviceSet) initDevmapper() error {
|
|
| 361 |
- logInit(devices) |
|
| 362 |
- |
|
| 363 |
- // Make sure the sparse images exist in <root>/devicemapper/data and |
|
| 364 |
- // <root>/devicemapper/metadata |
|
| 365 |
- |
|
| 366 |
- createdLoopback := !devices.hasImage("data") || !devices.hasImage("metadata")
|
|
| 367 |
- data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
|
|
| 368 |
- if err != nil {
|
|
| 369 |
- utils.Debugf("Error device ensureImage (data): %s\n", err)
|
|
| 370 |
- return err |
|
| 371 |
- } |
|
| 372 |
- metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
|
|
| 373 |
- if err != nil {
|
|
| 374 |
- utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
|
| 375 |
- return err |
|
| 376 |
- } |
|
| 377 |
- |
|
| 378 |
- // Set the device prefix from the device id and inode of the docker root dir |
|
| 379 |
- |
|
| 380 |
- st, err := os.Stat(devices.root) |
|
| 381 |
- if err != nil {
|
|
| 382 |
- return fmt.Errorf("Error looking up dir %s: %s", devices.root, err)
|
|
| 383 |
- } |
|
| 384 |
- sysSt := st.Sys().(*syscall.Stat_t) |
|
| 385 |
- // "reg-" stands for "regular file". |
|
| 386 |
- // In the future we might use "dev-" for "device file", etc. |
|
| 387 |
- // docker-maj,min[-inode] stands for: |
|
| 388 |
- // - Managed by docker |
|
| 389 |
- // - The target of this device is at major <maj> and minor <min> |
|
| 390 |
- // - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself. |
|
| 391 |
- devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
|
|
| 392 |
- utils.Debugf("Generated prefix: %s", devices.devicePrefix)
|
|
| 393 |
- |
|
| 394 |
- // Check for the existence of the device <prefix>-pool |
|
| 395 |
- utils.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
|
|
| 396 |
- info, err := getInfo(devices.getPoolName()) |
|
| 397 |
- if info == nil {
|
|
| 398 |
- utils.Debugf("Error device getInfo: %s", err)
|
|
| 399 |
- return err |
|
| 400 |
- } |
|
| 401 |
- |
|
| 402 |
- // It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files |
|
| 403 |
- // that are not Close-on-exec, and lxc-start will die if it inherits any unexpected files, |
|
| 404 |
- // so we add this badhack to make sure it closes itself |
|
| 405 |
- setCloseOnExec("/dev/mapper/control")
|
|
| 406 |
- |
|
| 407 |
- // If the pool doesn't exist, create it |
|
| 408 |
- if info.Exists == 0 {
|
|
| 409 |
- utils.Debugf("Pool doesn't exist. Creating it.")
|
|
| 410 |
- |
|
| 411 |
- dataFile, err := AttachLoopDevice(data) |
|
| 412 |
- if err != nil {
|
|
| 413 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 414 |
- return err |
|
| 415 |
- } |
|
| 416 |
- defer dataFile.Close() |
|
| 417 |
- |
|
| 418 |
- metadataFile, err := AttachLoopDevice(metadata) |
|
| 419 |
- if err != nil {
|
|
| 420 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 421 |
- return err |
|
| 422 |
- } |
|
| 423 |
- defer metadataFile.Close() |
|
| 424 |
- |
|
| 425 |
- if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
|
|
| 426 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 427 |
- return err |
|
| 428 |
- } |
|
| 429 |
- } |
|
| 430 |
- |
|
| 431 |
- // If we didn't just create the data or metadata image, we need to |
|
| 432 |
- // load the metadata from the existing file. |
|
| 433 |
- if !createdLoopback {
|
|
| 434 |
- if err = devices.loadMetaData(); err != nil {
|
|
| 435 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 436 |
- return err |
|
| 437 |
- } |
|
| 438 |
- } |
|
| 439 |
- |
|
| 440 |
- // Setup the base image |
|
| 441 |
- if err := devices.setupBaseImage(); err != nil {
|
|
| 442 |
- utils.Debugf("Error device setupBaseImage: %s\n", err)
|
|
| 443 |
- return err |
|
| 444 |
- } |
|
| 445 |
- |
|
| 446 |
- return nil |
|
| 447 |
-} |
|
| 448 |
- |
|
| 449 |
-func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
|
|
| 450 |
- devices.Lock() |
|
| 451 |
- defer devices.Unlock() |
|
| 452 |
- |
|
| 453 |
- if devices.Devices[hash] != nil {
|
|
| 454 |
- return fmt.Errorf("hash %s already exists", hash)
|
|
| 455 |
- } |
|
| 456 |
- |
|
| 457 |
- baseInfo := devices.Devices[baseHash] |
|
| 458 |
- if baseInfo == nil {
|
|
| 459 |
- return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
|
|
| 460 |
- } |
|
| 461 |
- |
|
| 462 |
- deviceId := devices.allocateDeviceId() |
|
| 463 |
- |
|
| 464 |
- if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
|
|
| 465 |
- utils.Debugf("Error creating snap device: %s\n", err)
|
|
| 466 |
- return err |
|
| 467 |
- } |
|
| 468 |
- |
|
| 469 |
- if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
|
|
| 470 |
- deleteDevice(devices.getPoolDevName(), deviceId) |
|
| 471 |
- utils.Debugf("Error registering device: %s\n", err)
|
|
| 472 |
- return err |
|
| 473 |
- } |
|
| 474 |
- return nil |
|
| 475 |
-} |
|
| 476 |
- |
|
| 477 |
-func (devices *DeviceSet) removeDevice(hash string) error {
|
|
| 478 |
- info := devices.Devices[hash] |
|
| 479 |
- if info == nil {
|
|
| 480 |
- return fmt.Errorf("hash %s doesn't exists", hash)
|
|
| 481 |
- } |
|
| 482 |
- |
|
| 483 |
- devinfo, _ := getInfo(info.Name()) |
|
| 484 |
- if devinfo != nil && devinfo.Exists != 0 {
|
|
| 485 |
- if err := removeDevice(info.Name()); err != nil {
|
|
| 486 |
- utils.Debugf("Error removing device: %s\n", err)
|
|
| 487 |
- return err |
|
| 488 |
- } |
|
| 489 |
- } |
|
| 490 |
- |
|
| 491 |
- if info.Initialized {
|
|
| 492 |
- info.Initialized = false |
|
| 493 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 494 |
- utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 495 |
- return err |
|
| 496 |
- } |
|
| 497 |
- } |
|
| 498 |
- |
|
| 499 |
- if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
|
|
| 500 |
- utils.Debugf("Error deleting device: %s\n", err)
|
|
| 501 |
- return err |
|
| 502 |
- } |
|
| 503 |
- |
|
| 504 |
- devices.allocateTransactionId() |
|
| 505 |
- delete(devices.Devices, info.Hash) |
|
| 506 |
- |
|
| 507 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 508 |
- devices.Devices[info.Hash] = info |
|
| 509 |
- utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 510 |
- return err |
|
| 511 |
- } |
|
| 512 |
- |
|
| 513 |
- return nil |
|
| 514 |
-} |
|
| 515 |
- |
|
| 516 |
-func (devices *DeviceSet) RemoveDevice(hash string) error {
|
|
| 517 |
- devices.Lock() |
|
| 518 |
- defer devices.Unlock() |
|
| 519 |
- |
|
| 520 |
- return devices.removeDevice(hash) |
|
| 521 |
-} |
|
| 522 |
- |
|
| 523 |
-func (devices *DeviceSet) deactivateDevice(hash string) error {
|
|
| 524 |
- utils.Debugf("[devmapper] deactivateDevice(%s)", hash)
|
|
| 525 |
- defer utils.Debugf("[devmapper] deactivateDevice END")
|
|
| 526 |
- var devname string |
|
| 527 |
- // FIXME: shouldn't we just register the pool into devices? |
|
| 528 |
- devname, err := devices.byHash(hash) |
|
| 529 |
- if err != nil {
|
|
| 530 |
- return err |
|
| 531 |
- } |
|
| 532 |
- devinfo, err := getInfo(devname) |
|
| 533 |
- if err != nil {
|
|
| 534 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 535 |
- return err |
|
| 536 |
- } |
|
| 537 |
- if devinfo.Exists != 0 {
|
|
| 538 |
- if err := removeDevice(devname); err != nil {
|
|
| 539 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 540 |
- return err |
|
| 541 |
- } |
|
| 542 |
- if err := devices.waitRemove(hash); err != nil {
|
|
| 543 |
- return err |
|
| 544 |
- } |
|
| 545 |
- } |
|
| 546 |
- |
|
| 547 |
- return nil |
|
| 548 |
-} |
|
| 549 |
- |
|
| 550 |
-// waitRemove blocks until either: |
|
| 551 |
-// a) the device registered at <device_set_prefix>-<hash> is removed, |
|
| 552 |
-// or b) the 1 second timeout expires. |
|
| 553 |
-func (devices *DeviceSet) waitRemove(hash string) error {
|
|
| 554 |
- utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, hash)
|
|
| 555 |
- defer utils.Debugf("[deviceset %s] waitRemove END", devices.devicePrefix, hash)
|
|
| 556 |
- devname, err := devices.byHash(hash) |
|
| 557 |
- if err != nil {
|
|
| 558 |
- return err |
|
| 559 |
- } |
|
| 560 |
- i := 0 |
|
| 561 |
- for ; i < 1000; i += 1 {
|
|
| 562 |
- devinfo, err := getInfo(devname) |
|
| 563 |
- if err != nil {
|
|
| 564 |
- // If there is an error we assume the device doesn't exist. |
|
| 565 |
- // The error might actually be something else, but we can't differentiate. |
|
| 566 |
- return nil |
|
| 567 |
- } |
|
| 568 |
- utils.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
|
|
| 569 |
- if devinfo.Exists == 0 {
|
|
| 570 |
- break |
|
| 571 |
- } |
|
| 572 |
- time.Sleep(1 * time.Millisecond) |
|
| 573 |
- } |
|
| 574 |
- if i == 1000 {
|
|
| 575 |
- return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
|
|
| 576 |
- } |
|
| 577 |
- return nil |
|
| 578 |
-} |
|
| 579 |
- |
|
| 580 |
-// waitClose blocks until either: |
|
| 581 |
-// a) the device registered at <device_set_prefix>-<hash> is closed, |
|
| 582 |
-// or b) the 1 second timeout expires. |
|
| 583 |
-func (devices *DeviceSet) waitClose(hash string) error {
|
|
| 584 |
- devname, err := devices.byHash(hash) |
|
| 585 |
- if err != nil {
|
|
| 586 |
- return err |
|
| 587 |
- } |
|
| 588 |
- i := 0 |
|
| 589 |
- for ; i < 1000; i += 1 {
|
|
| 590 |
- devinfo, err := getInfo(devname) |
|
| 591 |
- if err != nil {
|
|
| 592 |
- return err |
|
| 593 |
- } |
|
| 594 |
- utils.Debugf("Waiting for unmount of %s: opencount=%d", devname, devinfo.OpenCount)
|
|
| 595 |
- if devinfo.OpenCount == 0 {
|
|
| 596 |
- break |
|
| 597 |
- } |
|
| 598 |
- time.Sleep(1 * time.Millisecond) |
|
| 599 |
- } |
|
| 600 |
- if i == 1000 {
|
|
| 601 |
- return fmt.Errorf("Timeout while waiting for device %s to close", devname)
|
|
| 602 |
- } |
|
| 603 |
- return nil |
|
| 604 |
-} |
|
| 605 |
- |
|
| 606 |
-// byHash is a hack to allow looking up the deviceset's pool by the hash "pool". |
|
| 607 |
-// FIXME: it seems probably cleaner to register the pool in devices.Devices, |
|
| 608 |
-// but I am afraid of arcane implications deep in the devicemapper code, |
|
| 609 |
-// so this will do. |
|
| 610 |
-func (devices *DeviceSet) byHash(hash string) (devname string, err error) {
|
|
| 611 |
- if hash == "pool" {
|
|
| 612 |
- return devices.getPoolDevName(), nil |
|
| 613 |
- } |
|
| 614 |
- info := devices.Devices[hash] |
|
| 615 |
- if info == nil {
|
|
| 616 |
- return "", fmt.Errorf("hash %s doesn't exists", hash)
|
|
| 617 |
- } |
|
| 618 |
- return info.Name(), nil |
|
| 619 |
-} |
|
| 620 |
- |
|
| 621 |
-func (devices *DeviceSet) Shutdown() error {
|
|
| 622 |
- utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
|
|
| 623 |
- defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
|
|
| 624 |
- devices.Lock() |
|
| 625 |
- utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
|
|
| 626 |
- defer devices.Unlock() |
|
| 627 |
- |
|
| 628 |
- for path, count := range devices.activeMounts {
|
|
| 629 |
- for i := count; i > 0; i-- {
|
|
| 630 |
- if err := syscall.Unmount(path, 0); err != nil {
|
|
| 631 |
- utils.Debugf("Shutdown unmounting %s, error: %s\n", path, err)
|
|
| 632 |
- } |
|
| 633 |
- } |
|
| 634 |
- delete(devices.activeMounts, path) |
|
| 635 |
- } |
|
| 636 |
- |
|
| 637 |
- for _, d := range devices.Devices {
|
|
| 638 |
- if err := devices.waitClose(d.Hash); err != nil {
|
|
| 639 |
- utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
|
|
| 640 |
- } |
|
| 641 |
- if err := devices.deactivateDevice(d.Hash); err != nil {
|
|
| 642 |
- utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
|
|
| 643 |
- } |
|
| 644 |
- } |
|
| 645 |
- |
|
| 646 |
- pool := devices.getPoolDevName() |
|
| 647 |
- if devinfo, err := getInfo(pool); err == nil && devinfo.Exists != 0 {
|
|
| 648 |
- if err := devices.deactivateDevice("pool"); err != nil {
|
|
| 649 |
- utils.Debugf("Shutdown deactivate %s , error: %s\n", pool, err)
|
|
| 650 |
- } |
|
| 651 |
- } |
|
| 652 |
- |
|
| 653 |
- return nil |
|
| 654 |
-} |
|
| 655 |
- |
|
| 656 |
-func (devices *DeviceSet) MountDevice(hash, path string, readOnly bool) error {
|
|
| 657 |
- devices.Lock() |
|
| 658 |
- defer devices.Unlock() |
|
| 659 |
- |
|
| 660 |
- if err := devices.activateDeviceIfNeeded(hash); err != nil {
|
|
| 661 |
- return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
|
|
| 662 |
- } |
|
| 663 |
- |
|
| 664 |
- info := devices.Devices[hash] |
|
| 665 |
- |
|
| 666 |
- var flags uintptr = syscall.MS_MGC_VAL |
|
| 667 |
- |
|
| 668 |
- if readOnly {
|
|
| 669 |
- flags = flags | syscall.MS_RDONLY |
|
| 670 |
- } |
|
| 671 |
- |
|
| 672 |
- err := syscall.Mount(info.DevName(), path, "ext4", flags, "discard") |
|
| 673 |
- if err != nil && err == syscall.EINVAL {
|
|
| 674 |
- err = syscall.Mount(info.DevName(), path, "ext4", flags, "") |
|
| 675 |
- } |
|
| 676 |
- if err != nil {
|
|
| 677 |
- return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
|
| 678 |
- } |
|
| 679 |
- |
|
| 680 |
- count := devices.activeMounts[path] |
|
| 681 |
- devices.activeMounts[path] = count + 1 |
|
| 682 |
- |
|
| 683 |
- return devices.setInitialized(hash) |
|
| 684 |
-} |
|
| 685 |
- |
|
| 686 |
-func (devices *DeviceSet) UnmountDevice(hash, path string, deactivate bool) error {
|
|
| 687 |
- utils.Debugf("[devmapper] UnmountDevice(hash=%s path=%s)", hash, path)
|
|
| 688 |
- defer utils.Debugf("[devmapper] UnmountDevice END")
|
|
| 689 |
- devices.Lock() |
|
| 690 |
- defer devices.Unlock() |
|
| 691 |
- |
|
| 692 |
- utils.Debugf("[devmapper] Unmount(%s)", path)
|
|
| 693 |
- if err := syscall.Unmount(path, 0); err != nil {
|
|
| 694 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 695 |
- return err |
|
| 696 |
- } |
|
| 697 |
- utils.Debugf("[devmapper] Unmount done")
|
|
| 698 |
- // Wait for the unmount to be effective, |
|
| 699 |
- // by watching the value of Info.OpenCount for the device |
|
| 700 |
- if err := devices.waitClose(hash); err != nil {
|
|
| 701 |
- return err |
|
| 702 |
- } |
|
| 703 |
- |
|
| 704 |
- if count := devices.activeMounts[path]; count > 1 {
|
|
| 705 |
- devices.activeMounts[path] = count - 1 |
|
| 706 |
- } else {
|
|
| 707 |
- delete(devices.activeMounts, path) |
|
| 708 |
- } |
|
| 709 |
- |
|
| 710 |
- if deactivate {
|
|
| 711 |
- devices.deactivateDevice(hash) |
|
| 712 |
- } |
|
| 713 |
- |
|
| 714 |
- return nil |
|
| 715 |
-} |
|
| 716 |
- |
|
| 717 |
-func (devices *DeviceSet) HasDevice(hash string) bool {
|
|
| 718 |
- devices.Lock() |
|
| 719 |
- defer devices.Unlock() |
|
| 720 |
- |
|
| 721 |
- return devices.Devices[hash] != nil |
|
| 722 |
-} |
|
| 723 |
- |
|
| 724 |
-func (devices *DeviceSet) HasInitializedDevice(hash string) bool {
|
|
| 725 |
- devices.Lock() |
|
| 726 |
- defer devices.Unlock() |
|
| 727 |
- |
|
| 728 |
- info := devices.Devices[hash] |
|
| 729 |
- return info != nil && info.Initialized |
|
| 730 |
-} |
|
| 731 |
- |
|
| 732 |
-func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
|
|
| 733 |
- devices.Lock() |
|
| 734 |
- defer devices.Unlock() |
|
| 735 |
- |
|
| 736 |
- info := devices.Devices[hash] |
|
| 737 |
- if info == nil {
|
|
| 738 |
- return false |
|
| 739 |
- } |
|
| 740 |
- devinfo, _ := getInfo(info.Name()) |
|
| 741 |
- return devinfo != nil && devinfo.Exists != 0 |
|
| 742 |
-} |
|
| 743 |
- |
|
| 744 |
-func (devices *DeviceSet) setInitialized(hash string) error {
|
|
| 745 |
- info := devices.Devices[hash] |
|
| 746 |
- if info == nil {
|
|
| 747 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 748 |
- } |
|
| 749 |
- |
|
| 750 |
- info.Initialized = true |
|
| 751 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 752 |
- info.Initialized = false |
|
| 753 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 754 |
- return err |
|
| 755 |
- } |
|
| 756 |
- |
|
| 757 |
- return nil |
|
| 758 |
-} |
|
| 759 |
- |
|
| 760 |
-func (devices *DeviceSet) Status() *Status {
|
|
| 761 |
- devices.Lock() |
|
| 762 |
- defer devices.Unlock() |
|
| 763 |
- |
|
| 764 |
- status := &Status{}
|
|
| 765 |
- |
|
| 766 |
- status.PoolName = devices.getPoolName() |
|
| 767 |
- status.DataLoopback = path.Join(devices.loopbackDir(), "data") |
|
| 768 |
- status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") |
|
| 769 |
- |
|
| 770 |
- _, totalSizeInSectors, _, params, err := getStatus(devices.getPoolName()) |
|
| 771 |
- if err == nil {
|
|
| 772 |
- var transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64 |
|
| 773 |
- if _, err := fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal); err == nil {
|
|
| 774 |
- // Convert from blocks to bytes |
|
| 775 |
- blockSizeInSectors := totalSizeInSectors / dataTotal |
|
| 776 |
- |
|
| 777 |
- status.Data.Used = dataUsed * blockSizeInSectors * 512 |
|
| 778 |
- status.Data.Total = dataTotal * blockSizeInSectors * 512 |
|
| 779 |
- |
|
| 780 |
- // metadata blocks are always 4k |
|
| 781 |
- status.Metadata.Used = metadataUsed * 4096 |
|
| 782 |
- status.Metadata.Total = metadataTotal * 4096 |
|
| 783 |
- } |
|
| 784 |
- } |
|
| 785 |
- |
|
| 786 |
- return status |
|
| 787 |
-} |
|
| 788 |
- |
|
| 789 |
-func NewDeviceSet(root string) (*DeviceSet, error) {
|
|
| 790 |
- SetDevDir("/dev")
|
|
| 791 |
- |
|
| 792 |
- devices := &DeviceSet{
|
|
| 793 |
- root: root, |
|
| 794 |
- MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
|
| 795 |
- activeMounts: make(map[string]int), |
|
| 796 |
- } |
|
| 797 |
- |
|
| 798 |
- if err := devices.initDevmapper(); err != nil {
|
|
| 799 |
- return nil, err |
|
| 800 |
- } |
|
| 801 |
- |
|
| 802 |
- return devices, nil |
|
| 803 |
-} |
| 804 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,505 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/dotcloud/docker/utils" |
|
| 7 |
- "os" |
|
| 8 |
- "runtime" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-type DevmapperLogger interface {
|
|
| 12 |
- log(level int, file string, line int, dmError int, message string) |
|
| 13 |
-} |
|
| 14 |
- |
|
| 15 |
-const ( |
|
| 16 |
- DeviceCreate TaskType = iota |
|
| 17 |
- DeviceReload |
|
| 18 |
- DeviceRemove |
|
| 19 |
- DeviceRemoveAll |
|
| 20 |
- DeviceSuspend |
|
| 21 |
- DeviceResume |
|
| 22 |
- DeviceInfo |
|
| 23 |
- DeviceDeps |
|
| 24 |
- DeviceRename |
|
| 25 |
- DeviceVersion |
|
| 26 |
- DeviceStatus |
|
| 27 |
- DeviceTable |
|
| 28 |
- DeviceWaitevent |
|
| 29 |
- DeviceList |
|
| 30 |
- DeviceClear |
|
| 31 |
- DeviceMknodes |
|
| 32 |
- DeviceListVersions |
|
| 33 |
- DeviceTargetMsg |
|
| 34 |
- DeviceSetGeometry |
|
| 35 |
-) |
|
| 36 |
- |
|
| 37 |
-const ( |
|
| 38 |
- AddNodeOnResume AddNodeType = iota |
|
| 39 |
- AddNodeOnCreate |
|
| 40 |
-) |
|
| 41 |
- |
|
| 42 |
-var ( |
|
| 43 |
- ErrTaskRun = errors.New("dm_task_run failed")
|
|
| 44 |
- ErrTaskSetName = errors.New("dm_task_set_name failed")
|
|
| 45 |
- ErrTaskSetMessage = errors.New("dm_task_set_message failed")
|
|
| 46 |
- ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed")
|
|
| 47 |
- ErrTaskSetRo = errors.New("dm_task_set_ro failed")
|
|
| 48 |
- ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
|
| 49 |
- ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
|
| 50 |
- ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
|
| 51 |
- ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
|
|
| 52 |
- ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
|
| 53 |
- ErrNilCookie = errors.New("cookie ptr can't be nil")
|
|
| 54 |
- ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
|
|
| 55 |
- ErrGetBlockSize = errors.New("Can't get block size")
|
|
| 56 |
- ErrUdevWait = errors.New("wait on udev cookie failed")
|
|
| 57 |
- ErrSetDevDir = errors.New("dm_set_dev_dir failed")
|
|
| 58 |
- ErrGetLibraryVersion = errors.New("dm_get_library_version failed")
|
|
| 59 |
- ErrCreateRemoveTask = errors.New("Can't create task of type DeviceRemove")
|
|
| 60 |
- ErrRunRemoveDevice = errors.New("running removeDevice failed")
|
|
| 61 |
- ErrInvalidAddNode = errors.New("Invalide AddNoce type")
|
|
| 62 |
-) |
|
| 63 |
- |
|
| 64 |
-type ( |
|
| 65 |
- Task struct {
|
|
| 66 |
- unmanaged *CDmTask |
|
| 67 |
- } |
|
| 68 |
- Info struct {
|
|
| 69 |
- Exists int |
|
| 70 |
- Suspended int |
|
| 71 |
- LiveTable int |
|
| 72 |
- InactiveTable int |
|
| 73 |
- OpenCount int32 |
|
| 74 |
- EventNr uint32 |
|
| 75 |
- Major uint32 |
|
| 76 |
- Minor uint32 |
|
| 77 |
- ReadOnly int |
|
| 78 |
- TargetCount int32 |
|
| 79 |
- } |
|
| 80 |
- TaskType int |
|
| 81 |
- AddNodeType int |
|
| 82 |
-) |
|
| 83 |
- |
|
| 84 |
-func (t *Task) destroy() {
|
|
| 85 |
- if t != nil {
|
|
| 86 |
- DmTaskDestory(t.unmanaged) |
|
| 87 |
- runtime.SetFinalizer(t, nil) |
|
| 88 |
- } |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func TaskCreate(tasktype TaskType) *Task {
|
|
| 92 |
- Ctask := DmTaskCreate(int(tasktype)) |
|
| 93 |
- if Ctask == nil {
|
|
| 94 |
- return nil |
|
| 95 |
- } |
|
| 96 |
- task := &Task{unmanaged: Ctask}
|
|
| 97 |
- runtime.SetFinalizer(task, (*Task).destroy) |
|
| 98 |
- return task |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-func (t *Task) Run() error {
|
|
| 102 |
- if res := DmTaskRun(t.unmanaged); res != 1 {
|
|
| 103 |
- return ErrTaskRun |
|
| 104 |
- } |
|
| 105 |
- return nil |
|
| 106 |
-} |
|
| 107 |
- |
|
| 108 |
-func (t *Task) SetName(name string) error {
|
|
| 109 |
- if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
|
| 110 |
- return ErrTaskSetName |
|
| 111 |
- } |
|
| 112 |
- return nil |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-func (t *Task) SetMessage(message string) error {
|
|
| 116 |
- if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
|
| 117 |
- return ErrTaskSetMessage |
|
| 118 |
- } |
|
| 119 |
- return nil |
|
| 120 |
-} |
|
| 121 |
- |
|
| 122 |
-func (t *Task) SetSector(sector uint64) error {
|
|
| 123 |
- if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
|
| 124 |
- return ErrTaskSetSector |
|
| 125 |
- } |
|
| 126 |
- return nil |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-func (t *Task) SetCookie(cookie *uint, flags uint16) error {
|
|
| 130 |
- if cookie == nil {
|
|
| 131 |
- return ErrNilCookie |
|
| 132 |
- } |
|
| 133 |
- if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
|
|
| 134 |
- return ErrTaskSetCookie |
|
| 135 |
- } |
|
| 136 |
- return nil |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-func (t *Task) SetAddNode(addNode AddNodeType) error {
|
|
| 140 |
- if addNode != AddNodeOnResume && addNode != AddNodeOnCreate {
|
|
| 141 |
- return ErrInvalidAddNode |
|
| 142 |
- } |
|
| 143 |
- if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
|
| 144 |
- return ErrTaskSetAddNode |
|
| 145 |
- } |
|
| 146 |
- return nil |
|
| 147 |
-} |
|
| 148 |
- |
|
| 149 |
-func (t *Task) SetRo() error {
|
|
| 150 |
- if res := DmTaskSetRo(t.unmanaged); res != 1 {
|
|
| 151 |
- return ErrTaskSetRo |
|
| 152 |
- } |
|
| 153 |
- return nil |
|
| 154 |
-} |
|
| 155 |
- |
|
| 156 |
-func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
|
|
| 157 |
- if res := DmTaskAddTarget(t.unmanaged, start, size, |
|
| 158 |
- ttype, params); res != 1 {
|
|
| 159 |
- return ErrTaskAddTarget |
|
| 160 |
- } |
|
| 161 |
- return nil |
|
| 162 |
-} |
|
| 163 |
- |
|
| 164 |
-func (t *Task) GetInfo() (*Info, error) {
|
|
| 165 |
- info := &Info{}
|
|
| 166 |
- if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
|
| 167 |
- return nil, ErrTaskGetInfo |
|
| 168 |
- } |
|
| 169 |
- return info, nil |
|
| 170 |
-} |
|
| 171 |
- |
|
| 172 |
-func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, |
|
| 173 |
- length uint64, targetType string, params string) {
|
|
| 174 |
- |
|
| 175 |
- return DmGetNextTarget(t.unmanaged, next, &start, &length, |
|
| 176 |
- &targetType, ¶ms), |
|
| 177 |
- start, length, targetType, params |
|
| 178 |
-} |
|
| 179 |
- |
|
| 180 |
-func AttachLoopDevice(filename string) (*os.File, error) {
|
|
| 181 |
- var fd int |
|
| 182 |
- res := DmAttachLoopDevice(filename, &fd) |
|
| 183 |
- if res == "" {
|
|
| 184 |
- return nil, ErrAttachLoopbackDevice |
|
| 185 |
- } |
|
| 186 |
- return os.NewFile(uintptr(fd), res), nil |
|
| 187 |
-} |
|
| 188 |
- |
|
| 189 |
-func UdevWait(cookie uint) error {
|
|
| 190 |
- if res := DmUdevWait(cookie); res != 1 {
|
|
| 191 |
- utils.Debugf("Failed to wait on udev cookie %d", cookie)
|
|
| 192 |
- return ErrUdevWait |
|
| 193 |
- } |
|
| 194 |
- return nil |
|
| 195 |
-} |
|
| 196 |
- |
|
| 197 |
-func LogInitVerbose(level int) {
|
|
| 198 |
- DmLogInitVerbose(level) |
|
| 199 |
-} |
|
| 200 |
- |
|
| 201 |
-var dmLogger DevmapperLogger = nil |
|
| 202 |
- |
|
| 203 |
-func logInit(logger DevmapperLogger) {
|
|
| 204 |
- dmLogger = logger |
|
| 205 |
- LogWithErrnoInit() |
|
| 206 |
-} |
|
| 207 |
- |
|
| 208 |
-func SetDevDir(dir string) error {
|
|
| 209 |
- if res := DmSetDevDir(dir); res != 1 {
|
|
| 210 |
- utils.Debugf("Error dm_set_dev_dir")
|
|
| 211 |
- return ErrSetDevDir |
|
| 212 |
- } |
|
| 213 |
- return nil |
|
| 214 |
-} |
|
| 215 |
- |
|
| 216 |
-func GetLibraryVersion() (string, error) {
|
|
| 217 |
- var version string |
|
| 218 |
- if res := DmGetLibraryVersion(&version); res != 1 {
|
|
| 219 |
- return "", ErrGetLibraryVersion |
|
| 220 |
- } |
|
| 221 |
- return version, nil |
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-// Useful helper for cleanup |
|
| 225 |
-func RemoveDevice(name string) error {
|
|
| 226 |
- task := TaskCreate(DeviceRemove) |
|
| 227 |
- if task == nil {
|
|
| 228 |
- return ErrCreateRemoveTask |
|
| 229 |
- } |
|
| 230 |
- if err := task.SetName(name); err != nil {
|
|
| 231 |
- utils.Debugf("Can't set task name %s", name)
|
|
| 232 |
- return err |
|
| 233 |
- } |
|
| 234 |
- if err := task.Run(); err != nil {
|
|
| 235 |
- return ErrRunRemoveDevice |
|
| 236 |
- } |
|
| 237 |
- return nil |
|
| 238 |
-} |
|
| 239 |
- |
|
| 240 |
-func GetBlockDeviceSize(file *os.File) (uint64, error) {
|
|
| 241 |
- size, errno := DmGetBlockSize(file.Fd()) |
|
| 242 |
- if size == -1 || errno != 0 {
|
|
| 243 |
- return 0, ErrGetBlockSize |
|
| 244 |
- } |
|
| 245 |
- return uint64(size), nil |
|
| 246 |
-} |
|
| 247 |
- |
|
| 248 |
-// This is the programmatic example of "dmsetup create" |
|
| 249 |
-func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error {
|
|
| 250 |
- task, err := createTask(DeviceCreate, poolName) |
|
| 251 |
- if task == nil {
|
|
| 252 |
- return err |
|
| 253 |
- } |
|
| 254 |
- |
|
| 255 |
- size, err := GetBlockDeviceSize(dataFile) |
|
| 256 |
- if err != nil {
|
|
| 257 |
- return fmt.Errorf("Can't get data size")
|
|
| 258 |
- } |
|
| 259 |
- |
|
| 260 |
- params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768" |
|
| 261 |
- if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 262 |
- return fmt.Errorf("Can't add target")
|
|
| 263 |
- } |
|
| 264 |
- |
|
| 265 |
- var cookie uint = 0 |
|
| 266 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 267 |
- return fmt.Errorf("Can't set cookie")
|
|
| 268 |
- } |
|
| 269 |
- |
|
| 270 |
- if err := task.Run(); err != nil {
|
|
| 271 |
- return fmt.Errorf("Error running DeviceCreate (createPool)")
|
|
| 272 |
- } |
|
| 273 |
- |
|
| 274 |
- UdevWait(cookie) |
|
| 275 |
- |
|
| 276 |
- return nil |
|
| 277 |
-} |
|
| 278 |
- |
|
| 279 |
-func createTask(t TaskType, name string) (*Task, error) {
|
|
| 280 |
- task := TaskCreate(t) |
|
| 281 |
- if task == nil {
|
|
| 282 |
- return nil, fmt.Errorf("Can't create task of type %d", int(t))
|
|
| 283 |
- } |
|
| 284 |
- if err := task.SetName(name); err != nil {
|
|
| 285 |
- return nil, fmt.Errorf("Can't set task name %s", name)
|
|
| 286 |
- } |
|
| 287 |
- return task, nil |
|
| 288 |
-} |
|
| 289 |
- |
|
| 290 |
-func getInfo(name string) (*Info, error) {
|
|
| 291 |
- task, err := createTask(DeviceInfo, name) |
|
| 292 |
- if task == nil {
|
|
| 293 |
- return nil, err |
|
| 294 |
- } |
|
| 295 |
- if err := task.Run(); err != nil {
|
|
| 296 |
- return nil, err |
|
| 297 |
- } |
|
| 298 |
- return task.GetInfo() |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 |
-func getStatus(name string) (uint64, uint64, string, string, error) {
|
|
| 302 |
- task, err := createTask(DeviceStatus, name) |
|
| 303 |
- if task == nil {
|
|
| 304 |
- utils.Debugf("getStatus: Error createTask: %s", err)
|
|
| 305 |
- return 0, 0, "", "", err |
|
| 306 |
- } |
|
| 307 |
- if err := task.Run(); err != nil {
|
|
| 308 |
- utils.Debugf("getStatus: Error Run: %s", err)
|
|
| 309 |
- return 0, 0, "", "", err |
|
| 310 |
- } |
|
| 311 |
- |
|
| 312 |
- devinfo, err := task.GetInfo() |
|
| 313 |
- if err != nil {
|
|
| 314 |
- utils.Debugf("getStatus: Error GetInfo: %s", err)
|
|
| 315 |
- return 0, 0, "", "", err |
|
| 316 |
- } |
|
| 317 |
- if devinfo.Exists == 0 {
|
|
| 318 |
- utils.Debugf("getStatus: Non existing device %s", name)
|
|
| 319 |
- return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
|
|
| 320 |
- } |
|
| 321 |
- |
|
| 322 |
- _, start, length, target_type, params := task.GetNextTarget(0) |
|
| 323 |
- return start, length, target_type, params, nil |
|
| 324 |
-} |
|
| 325 |
- |
|
| 326 |
-func setTransactionId(poolName string, oldId uint64, newId uint64) error {
|
|
| 327 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 328 |
- if task == nil {
|
|
| 329 |
- return err |
|
| 330 |
- } |
|
| 331 |
- |
|
| 332 |
- if err := task.SetSector(0); err != nil {
|
|
| 333 |
- return fmt.Errorf("Can't set sector")
|
|
| 334 |
- } |
|
| 335 |
- |
|
| 336 |
- if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
|
|
| 337 |
- return fmt.Errorf("Can't set message")
|
|
| 338 |
- } |
|
| 339 |
- |
|
| 340 |
- if err := task.Run(); err != nil {
|
|
| 341 |
- return fmt.Errorf("Error running setTransactionId")
|
|
| 342 |
- } |
|
| 343 |
- return nil |
|
| 344 |
-} |
|
| 345 |
- |
|
| 346 |
-func suspendDevice(name string) error {
|
|
| 347 |
- task, err := createTask(DeviceSuspend, name) |
|
| 348 |
- if task == nil {
|
|
| 349 |
- return err |
|
| 350 |
- } |
|
| 351 |
- if err := task.Run(); err != nil {
|
|
| 352 |
- return fmt.Errorf("Error running DeviceSuspend")
|
|
| 353 |
- } |
|
| 354 |
- return nil |
|
| 355 |
-} |
|
| 356 |
- |
|
| 357 |
-func resumeDevice(name string) error {
|
|
| 358 |
- task, err := createTask(DeviceResume, name) |
|
| 359 |
- if task == nil {
|
|
| 360 |
- return err |
|
| 361 |
- } |
|
| 362 |
- |
|
| 363 |
- var cookie uint = 0 |
|
| 364 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 365 |
- return fmt.Errorf("Can't set cookie")
|
|
| 366 |
- } |
|
| 367 |
- |
|
| 368 |
- if err := task.Run(); err != nil {
|
|
| 369 |
- return fmt.Errorf("Error running DeviceSuspend")
|
|
| 370 |
- } |
|
| 371 |
- |
|
| 372 |
- UdevWait(cookie) |
|
| 373 |
- |
|
| 374 |
- return nil |
|
| 375 |
-} |
|
| 376 |
- |
|
| 377 |
-func createDevice(poolName string, deviceId int) error {
|
|
| 378 |
- utils.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
|
|
| 379 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 380 |
- if task == nil {
|
|
| 381 |
- return err |
|
| 382 |
- } |
|
| 383 |
- |
|
| 384 |
- if err := task.SetSector(0); err != nil {
|
|
| 385 |
- return fmt.Errorf("Can't set sector")
|
|
| 386 |
- } |
|
| 387 |
- |
|
| 388 |
- if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
|
|
| 389 |
- return fmt.Errorf("Can't set message")
|
|
| 390 |
- } |
|
| 391 |
- |
|
| 392 |
- if err := task.Run(); err != nil {
|
|
| 393 |
- return fmt.Errorf("Error running createDevice")
|
|
| 394 |
- } |
|
| 395 |
- return nil |
|
| 396 |
-} |
|
| 397 |
- |
|
| 398 |
-func deleteDevice(poolName string, deviceId int) error {
|
|
| 399 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 400 |
- if task == nil {
|
|
| 401 |
- return err |
|
| 402 |
- } |
|
| 403 |
- |
|
| 404 |
- if err := task.SetSector(0); err != nil {
|
|
| 405 |
- return fmt.Errorf("Can't set sector")
|
|
| 406 |
- } |
|
| 407 |
- |
|
| 408 |
- if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
|
|
| 409 |
- return fmt.Errorf("Can't set message")
|
|
| 410 |
- } |
|
| 411 |
- |
|
| 412 |
- if err := task.Run(); err != nil {
|
|
| 413 |
- return fmt.Errorf("Error running deleteDevice")
|
|
| 414 |
- } |
|
| 415 |
- return nil |
|
| 416 |
-} |
|
| 417 |
- |
|
| 418 |
-func removeDevice(name string) error {
|
|
| 419 |
- utils.Debugf("[devmapper] removeDevice START")
|
|
| 420 |
- defer utils.Debugf("[devmapper] removeDevice END")
|
|
| 421 |
- task, err := createTask(DeviceRemove, name) |
|
| 422 |
- if task == nil {
|
|
| 423 |
- return err |
|
| 424 |
- } |
|
| 425 |
- if err = task.Run(); err != nil {
|
|
| 426 |
- return fmt.Errorf("Error running removeDevice")
|
|
| 427 |
- } |
|
| 428 |
- return nil |
|
| 429 |
-} |
|
| 430 |
- |
|
| 431 |
-func activateDevice(poolName string, name string, deviceId int, size uint64) error {
|
|
| 432 |
- task, err := createTask(DeviceCreate, name) |
|
| 433 |
- if task == nil {
|
|
| 434 |
- return err |
|
| 435 |
- } |
|
| 436 |
- |
|
| 437 |
- params := fmt.Sprintf("%s %d", poolName, deviceId)
|
|
| 438 |
- if err := task.AddTarget(0, size/512, "thin", params); err != nil {
|
|
| 439 |
- return fmt.Errorf("Can't add target")
|
|
| 440 |
- } |
|
| 441 |
- if err := task.SetAddNode(AddNodeOnCreate); err != nil {
|
|
| 442 |
- return fmt.Errorf("Can't add node")
|
|
| 443 |
- } |
|
| 444 |
- |
|
| 445 |
- var cookie uint = 0 |
|
| 446 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 447 |
- return fmt.Errorf("Can't set cookie")
|
|
| 448 |
- } |
|
| 449 |
- |
|
| 450 |
- if err := task.Run(); err != nil {
|
|
| 451 |
- return fmt.Errorf("Error running DeviceCreate (activateDevice)")
|
|
| 452 |
- } |
|
| 453 |
- |
|
| 454 |
- UdevWait(cookie) |
|
| 455 |
- |
|
| 456 |
- return nil |
|
| 457 |
-} |
|
| 458 |
- |
|
| 459 |
-func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
|
|
| 460 |
- devinfo, _ := getInfo(baseName) |
|
| 461 |
- doSuspend := devinfo != nil && devinfo.Exists != 0 |
|
| 462 |
- |
|
| 463 |
- if doSuspend {
|
|
| 464 |
- if err := suspendDevice(baseName); err != nil {
|
|
| 465 |
- return err |
|
| 466 |
- } |
|
| 467 |
- } |
|
| 468 |
- |
|
| 469 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 470 |
- if task == nil {
|
|
| 471 |
- if doSuspend {
|
|
| 472 |
- resumeDevice(baseName) |
|
| 473 |
- } |
|
| 474 |
- return err |
|
| 475 |
- } |
|
| 476 |
- |
|
| 477 |
- if err := task.SetSector(0); err != nil {
|
|
| 478 |
- if doSuspend {
|
|
| 479 |
- resumeDevice(baseName) |
|
| 480 |
- } |
|
| 481 |
- return fmt.Errorf("Can't set sector")
|
|
| 482 |
- } |
|
| 483 |
- |
|
| 484 |
- if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
|
|
| 485 |
- if doSuspend {
|
|
| 486 |
- resumeDevice(baseName) |
|
| 487 |
- } |
|
| 488 |
- return fmt.Errorf("Can't set message")
|
|
| 489 |
- } |
|
| 490 |
- |
|
| 491 |
- if err := task.Run(); err != nil {
|
|
| 492 |
- if doSuspend {
|
|
| 493 |
- resumeDevice(baseName) |
|
| 494 |
- } |
|
| 495 |
- return fmt.Errorf("Error running DeviceCreate (createSnapDevice)")
|
|
| 496 |
- } |
|
| 497 |
- |
|
| 498 |
- if doSuspend {
|
|
| 499 |
- if err := resumeDevice(baseName); err != nil {
|
|
| 500 |
- return err |
|
| 501 |
- } |
|
| 502 |
- } |
|
| 503 |
- |
|
| 504 |
- return nil |
|
| 505 |
-} |
| 506 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,13 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-import "C" |
|
| 4 |
- |
|
| 5 |
-// Due to the way cgo works this has to be in a separate file, as devmapper.go has |
|
| 6 |
-// definitions in the cgo block, which is incompatible with using "//export" |
|
| 7 |
- |
|
| 8 |
-//export DevmapperLogCallback |
|
| 9 |
-func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_class C.int, message *C.char) {
|
|
| 10 |
- if dmLogger != nil {
|
|
| 11 |
- dmLogger.log(int(level), C.GoString(file), int(line), int(dm_errno_or_class), C.GoString(message)) |
|
| 12 |
- } |
|
| 13 |
-} |
| 14 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,285 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "syscall" |
|
| 5 |
- "testing" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-func TestTaskCreate(t *testing.T) {
|
|
| 9 |
- // Test success |
|
| 10 |
- taskCreate(t, DeviceInfo) |
|
| 11 |
- |
|
| 12 |
- // Test Failure |
|
| 13 |
- DmTaskCreate = dmTaskCreateFail |
|
| 14 |
- defer func() { DmTaskCreate = dmTaskCreateFct }()
|
|
| 15 |
- if task := TaskCreate(-1); task != nil {
|
|
| 16 |
- t.Fatalf("An error should have occured while creating an invalid task.")
|
|
| 17 |
- } |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-func TestTaskRun(t *testing.T) {
|
|
| 21 |
- task := taskCreate(t, DeviceInfo) |
|
| 22 |
- |
|
| 23 |
- // Test success |
|
| 24 |
- // Perform the RUN |
|
| 25 |
- if err := task.Run(); err != nil {
|
|
| 26 |
- t.Fatal(err) |
|
| 27 |
- } |
|
| 28 |
- // Make sure we don't have error with GetInfo |
|
| 29 |
- if _, err := task.GetInfo(); err != nil {
|
|
| 30 |
- t.Fatal(err) |
|
| 31 |
- } |
|
| 32 |
- |
|
| 33 |
- // Test failure |
|
| 34 |
- DmTaskRun = dmTaskRunFail |
|
| 35 |
- defer func() { DmTaskRun = dmTaskRunFct }()
|
|
| 36 |
- |
|
| 37 |
- task = taskCreate(t, DeviceInfo) |
|
| 38 |
- // Perform the RUN |
|
| 39 |
- if err := task.Run(); err != ErrTaskRun {
|
|
| 40 |
- t.Fatalf("An error should have occured while running task.")
|
|
| 41 |
- } |
|
| 42 |
- // Make sure GetInfo also fails |
|
| 43 |
- if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 44 |
- t.Fatalf("GetInfo should fail if task.Run() failed.")
|
|
| 45 |
- } |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func TestTaskSetName(t *testing.T) {
|
|
| 49 |
- task := taskCreate(t, DeviceInfo) |
|
| 50 |
- |
|
| 51 |
- // Test success |
|
| 52 |
- if err := task.SetName("test"); err != nil {
|
|
| 53 |
- t.Fatal(err) |
|
| 54 |
- } |
|
| 55 |
- |
|
| 56 |
- // Test failure |
|
| 57 |
- DmTaskSetName = dmTaskSetNameFail |
|
| 58 |
- defer func() { DmTaskSetName = dmTaskSetNameFct }()
|
|
| 59 |
- |
|
| 60 |
- if err := task.SetName("test"); err != ErrTaskSetName {
|
|
| 61 |
- t.Fatalf("An error should have occured while runnign SetName.")
|
|
| 62 |
- } |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-func TestTaskSetMessage(t *testing.T) {
|
|
| 66 |
- task := taskCreate(t, DeviceInfo) |
|
| 67 |
- |
|
| 68 |
- // Test success |
|
| 69 |
- if err := task.SetMessage("test"); err != nil {
|
|
| 70 |
- t.Fatal(err) |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- // Test failure |
|
| 74 |
- DmTaskSetMessage = dmTaskSetMessageFail |
|
| 75 |
- defer func() { DmTaskSetMessage = dmTaskSetMessageFct }()
|
|
| 76 |
- |
|
| 77 |
- if err := task.SetMessage("test"); err != ErrTaskSetMessage {
|
|
| 78 |
- t.Fatalf("An error should have occured while runnign SetMessage.")
|
|
| 79 |
- } |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func TestTaskSetSector(t *testing.T) {
|
|
| 83 |
- task := taskCreate(t, DeviceInfo) |
|
| 84 |
- |
|
| 85 |
- // Test success |
|
| 86 |
- if err := task.SetSector(128); err != nil {
|
|
| 87 |
- t.Fatal(err) |
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- DmTaskSetSector = dmTaskSetSectorFail |
|
| 91 |
- defer func() { DmTaskSetSector = dmTaskSetSectorFct }()
|
|
| 92 |
- |
|
| 93 |
- // Test failure |
|
| 94 |
- if err := task.SetSector(0); err != ErrTaskSetSector {
|
|
| 95 |
- t.Fatalf("An error should have occured while running SetSector.")
|
|
| 96 |
- } |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-func TestTaskSetCookie(t *testing.T) {
|
|
| 100 |
- var ( |
|
| 101 |
- cookie uint = 0 |
|
| 102 |
- task = taskCreate(t, DeviceInfo) |
|
| 103 |
- ) |
|
| 104 |
- |
|
| 105 |
- // Test success |
|
| 106 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 107 |
- t.Fatal(err) |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- // Test failure |
|
| 111 |
- if err := task.SetCookie(nil, 0); err != ErrNilCookie {
|
|
| 112 |
- t.Fatalf("An error should have occured while running SetCookie with nil cookie.")
|
|
| 113 |
- } |
|
| 114 |
- |
|
| 115 |
- DmTaskSetCookie = dmTaskSetCookieFail |
|
| 116 |
- defer func() { DmTaskSetCookie = dmTaskSetCookieFct }()
|
|
| 117 |
- |
|
| 118 |
- if err := task.SetCookie(&cookie, 0); err != ErrTaskSetCookie {
|
|
| 119 |
- t.Fatalf("An error should have occured while running SetCookie.")
|
|
| 120 |
- } |
|
| 121 |
-} |
|
| 122 |
- |
|
| 123 |
-func TestTaskSetAddNode(t *testing.T) {
|
|
| 124 |
- task := taskCreate(t, DeviceInfo) |
|
| 125 |
- |
|
| 126 |
- // Test success |
|
| 127 |
- if err := task.SetAddNode(0); err != nil {
|
|
| 128 |
- t.Fatal(err) |
|
| 129 |
- } |
|
| 130 |
- |
|
| 131 |
- // Test failure |
|
| 132 |
- if err := task.SetAddNode(-1); err != ErrInvalidAddNode {
|
|
| 133 |
- t.Fatalf("An error should have occured running SetAddNode with wrong node.")
|
|
| 134 |
- } |
|
| 135 |
- |
|
| 136 |
- DmTaskSetAddNode = dmTaskSetAddNodeFail |
|
| 137 |
- defer func() { DmTaskSetAddNode = dmTaskSetAddNodeFct }()
|
|
| 138 |
- |
|
| 139 |
- if err := task.SetAddNode(0); err != ErrTaskSetAddNode {
|
|
| 140 |
- t.Fatalf("An error should have occured running SetAddNode.")
|
|
| 141 |
- } |
|
| 142 |
-} |
|
| 143 |
- |
|
| 144 |
-func TestTaskSetRo(t *testing.T) {
|
|
| 145 |
- task := taskCreate(t, DeviceInfo) |
|
| 146 |
- |
|
| 147 |
- // Test success |
|
| 148 |
- if err := task.SetRo(); err != nil {
|
|
| 149 |
- t.Fatal(err) |
|
| 150 |
- } |
|
| 151 |
- |
|
| 152 |
- // Test failure |
|
| 153 |
- DmTaskSetRo = dmTaskSetRoFail |
|
| 154 |
- defer func() { DmTaskSetRo = dmTaskSetRoFct }()
|
|
| 155 |
- |
|
| 156 |
- if err := task.SetRo(); err != ErrTaskSetRo {
|
|
| 157 |
- t.Fatalf("An error should have occured running SetRo.")
|
|
| 158 |
- } |
|
| 159 |
-} |
|
| 160 |
- |
|
| 161 |
-func TestTaskAddTarget(t *testing.T) {
|
|
| 162 |
- task := taskCreate(t, DeviceInfo) |
|
| 163 |
- |
|
| 164 |
- // Test success |
|
| 165 |
- if err := task.AddTarget(0, 128, "thinp", ""); err != nil {
|
|
| 166 |
- t.Fatal(err) |
|
| 167 |
- } |
|
| 168 |
- |
|
| 169 |
- // Test failure |
|
| 170 |
- DmTaskAddTarget = dmTaskAddTargetFail |
|
| 171 |
- defer func() { DmTaskAddTarget = dmTaskAddTargetFct }()
|
|
| 172 |
- |
|
| 173 |
- if err := task.AddTarget(0, 128, "thinp", ""); err != ErrTaskAddTarget {
|
|
| 174 |
- t.Fatalf("An error should have occured running AddTarget.")
|
|
| 175 |
- } |
|
| 176 |
-} |
|
| 177 |
- |
|
| 178 |
-// func TestTaskGetInfo(t *testing.T) {
|
|
| 179 |
-// task := taskCreate(t, DeviceInfo) |
|
| 180 |
- |
|
| 181 |
-// // Test success |
|
| 182 |
-// if _, err := task.GetInfo(); err != nil {
|
|
| 183 |
-// t.Fatal(err) |
|
| 184 |
-// } |
|
| 185 |
- |
|
| 186 |
-// // Test failure |
|
| 187 |
-// DmTaskGetInfo = dmTaskGetInfoFail |
|
| 188 |
-// defer func() { DmTaskGetInfo = dmTaskGetInfoFct }()
|
|
| 189 |
- |
|
| 190 |
-// if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 191 |
-// t.Fatalf("An error should have occured running GetInfo.")
|
|
| 192 |
-// } |
|
| 193 |
-// } |
|
| 194 |
- |
|
| 195 |
-// func TestTaskGetNextTarget(t *testing.T) {
|
|
| 196 |
-// task := taskCreate(t, DeviceInfo) |
|
| 197 |
- |
|
| 198 |
-// if next, _, _, _, _ := task.GetNextTarget(0); next == 0 {
|
|
| 199 |
-// t.Fatalf("The next target should not be 0.")
|
|
| 200 |
-// } |
|
| 201 |
-// } |
|
| 202 |
- |
|
| 203 |
-/// Utils |
|
| 204 |
-func taskCreate(t *testing.T, taskType TaskType) *Task {
|
|
| 205 |
- task := TaskCreate(taskType) |
|
| 206 |
- if task == nil {
|
|
| 207 |
- t.Fatalf("Error creating task")
|
|
| 208 |
- } |
|
| 209 |
- return task |
|
| 210 |
-} |
|
| 211 |
- |
|
| 212 |
-/// Failure function replacement |
|
| 213 |
-func dmTaskCreateFail(t int) *CDmTask {
|
|
| 214 |
- return nil |
|
| 215 |
-} |
|
| 216 |
- |
|
| 217 |
-func dmTaskRunFail(task *CDmTask) int {
|
|
| 218 |
- return -1 |
|
| 219 |
-} |
|
| 220 |
- |
|
| 221 |
-func dmTaskSetNameFail(task *CDmTask, name string) int {
|
|
| 222 |
- return -1 |
|
| 223 |
-} |
|
| 224 |
- |
|
| 225 |
-func dmTaskSetMessageFail(task *CDmTask, message string) int {
|
|
| 226 |
- return -1 |
|
| 227 |
-} |
|
| 228 |
- |
|
| 229 |
-func dmTaskSetSectorFail(task *CDmTask, sector uint64) int {
|
|
| 230 |
- return -1 |
|
| 231 |
-} |
|
| 232 |
- |
|
| 233 |
-func dmTaskSetCookieFail(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 234 |
- return -1 |
|
| 235 |
-} |
|
| 236 |
- |
|
| 237 |
-func dmTaskSetAddNodeFail(task *CDmTask, addNode AddNodeType) int {
|
|
| 238 |
- return -1 |
|
| 239 |
-} |
|
| 240 |
- |
|
| 241 |
-func dmTaskSetRoFail(task *CDmTask) int {
|
|
| 242 |
- return -1 |
|
| 243 |
-} |
|
| 244 |
- |
|
| 245 |
-func dmTaskAddTargetFail(task *CDmTask, |
|
| 246 |
- start, size uint64, ttype, params string) int {
|
|
| 247 |
- return -1 |
|
| 248 |
-} |
|
| 249 |
- |
|
| 250 |
-func dmTaskGetDriverVersionFail(task *CDmTask, version *string) int {
|
|
| 251 |
- return -1 |
|
| 252 |
-} |
|
| 253 |
- |
|
| 254 |
-func dmTaskGetInfoFail(task *CDmTask, info *Info) int {
|
|
| 255 |
- return -1 |
|
| 256 |
-} |
|
| 257 |
- |
|
| 258 |
-func dmGetNextTargetFail(task *CDmTask, next uintptr, start, length *uint64, |
|
| 259 |
- target, params *string) uintptr {
|
|
| 260 |
- return 0 |
|
| 261 |
-} |
|
| 262 |
- |
|
| 263 |
-func dmAttachLoopDeviceFail(filename string, fd *int) string {
|
|
| 264 |
- return "" |
|
| 265 |
-} |
|
| 266 |
- |
|
| 267 |
-func sysGetBlockSizeFail(fd uintptr, size *uint64) syscall.Errno {
|
|
| 268 |
- return 1 |
|
| 269 |
-} |
|
| 270 |
- |
|
| 271 |
-func dmGetBlockSizeFail(fd uintptr) int64 {
|
|
| 272 |
- return -1 |
|
| 273 |
-} |
|
| 274 |
- |
|
| 275 |
-func dmUdevWaitFail(cookie uint) int {
|
|
| 276 |
- return -1 |
|
| 277 |
-} |
|
| 278 |
- |
|
| 279 |
-func dmSetDevDirFail(dir string) int {
|
|
| 280 |
- return -1 |
|
| 281 |
-} |
|
| 282 |
- |
|
| 283 |
-func dmGetLibraryVersionFail(version *string) int {
|
|
| 284 |
- return -1 |
|
| 285 |
-} |
| 286 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,330 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-/* |
|
| 4 |
-#cgo LDFLAGS: -L. -ldevmapper |
|
| 5 |
-#include <stdio.h> |
|
| 6 |
-#include <stdlib.h> |
|
| 7 |
-#include <unistd.h> |
|
| 8 |
-#include <libdevmapper.h> |
|
| 9 |
-#include <linux/loop.h> |
|
| 10 |
-#include <sys/types.h> |
|
| 11 |
-#include <sys/stat.h> |
|
| 12 |
-#include <fcntl.h> |
|
| 13 |
-#include <sys/ioctl.h> |
|
| 14 |
-#include <linux/fs.h> |
|
| 15 |
-#include <errno.h> |
|
| 16 |
- |
|
| 17 |
-#ifndef LOOP_CTL_GET_FREE |
|
| 18 |
-#define LOOP_CTL_GET_FREE 0x4C82 |
|
| 19 |
-#endif |
|
| 20 |
- |
|
| 21 |
-// FIXME: this could easily be rewritten in go |
|
| 22 |
-char* attach_loop_device(const char *filename, int *loop_fd_out) |
|
| 23 |
-{
|
|
| 24 |
- struct loop_info64 loopinfo = {0};
|
|
| 25 |
- struct stat st; |
|
| 26 |
- char buf[64]; |
|
| 27 |
- int i, loop_fd, fd, start_index; |
|
| 28 |
- char* loopname; |
|
| 29 |
- |
|
| 30 |
- |
|
| 31 |
- *loop_fd_out = -1; |
|
| 32 |
- |
|
| 33 |
- start_index = 0; |
|
| 34 |
- fd = open("/dev/loop-control", O_RDONLY);
|
|
| 35 |
- if (fd >= 0) {
|
|
| 36 |
- start_index = ioctl(fd, LOOP_CTL_GET_FREE); |
|
| 37 |
- close(fd); |
|
| 38 |
- |
|
| 39 |
- if (start_index < 0) |
|
| 40 |
- start_index = 0; |
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 |
- fd = open(filename, O_RDWR); |
|
| 44 |
- if (fd < 0) {
|
|
| 45 |
- perror("open");
|
|
| 46 |
- return NULL; |
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 |
- loop_fd = -1; |
|
| 50 |
- for (i = start_index ; loop_fd < 0 ; i++ ) {
|
|
| 51 |
- if (sprintf(buf, "/dev/loop%d", i) < 0) {
|
|
| 52 |
- close(fd); |
|
| 53 |
- return NULL; |
|
| 54 |
- } |
|
| 55 |
- |
|
| 56 |
- if (stat(buf, &st)) {
|
|
| 57 |
- if (!S_ISBLK(st.st_mode)) {
|
|
| 58 |
- fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf); |
|
| 59 |
- } else if (errno == ENOENT) {
|
|
| 60 |
- fprintf(stderr, "[error] There are no more loopback device available.\n"); |
|
| 61 |
- } else {
|
|
| 62 |
- fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno); |
|
| 63 |
- } |
|
| 64 |
- close(fd); |
|
| 65 |
- return NULL; |
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- loop_fd = open(buf, O_RDWR); |
|
| 69 |
- if (loop_fd < 0 && errno == ENOENT) {
|
|
| 70 |
- fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf); |
|
| 71 |
- close(fd); |
|
| 72 |
- return NULL; |
|
| 73 |
- } else if (loop_fd < 0) {
|
|
| 74 |
- fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno); |
|
| 75 |
- continue; |
|
| 76 |
- } |
|
| 77 |
- |
|
| 78 |
- if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) {
|
|
| 79 |
- int errsv = errno; |
|
| 80 |
- close(loop_fd); |
|
| 81 |
- loop_fd = -1; |
|
| 82 |
- if (errsv != EBUSY) {
|
|
| 83 |
- close(fd); |
|
| 84 |
- fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv)); |
|
| 85 |
- return NULL; |
|
| 86 |
- } |
|
| 87 |
- continue; |
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- close(fd); |
|
| 91 |
- |
|
| 92 |
- strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE); |
|
| 93 |
- loopinfo.lo_offset = 0; |
|
| 94 |
- loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR; |
|
| 95 |
- |
|
| 96 |
- if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) {
|
|
| 97 |
- perror("ioctl LOOP_SET_STATUS64");
|
|
| 98 |
- if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
|
|
| 99 |
- perror("ioctl LOOP_CLR_FD");
|
|
| 100 |
- } |
|
| 101 |
- close(loop_fd); |
|
| 102 |
- fprintf (stderr, "cannot set up loopback device info"); |
|
| 103 |
- return (NULL); |
|
| 104 |
- } |
|
| 105 |
- |
|
| 106 |
- loopname = strdup(buf); |
|
| 107 |
- if (loopname == NULL) {
|
|
| 108 |
- close(loop_fd); |
|
| 109 |
- return (NULL); |
|
| 110 |
- } |
|
| 111 |
- |
|
| 112 |
- *loop_fd_out = loop_fd; |
|
| 113 |
- return (loopname); |
|
| 114 |
- } |
|
| 115 |
- |
|
| 116 |
- return (NULL); |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 |
-extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); |
|
| 120 |
- |
|
| 121 |
-static void log_cb(int level, const char *file, int line, |
|
| 122 |
- int dm_errno_or_class, const char *f, ...) |
|
| 123 |
-{
|
|
| 124 |
- char buffer[256]; |
|
| 125 |
- va_list ap; |
|
| 126 |
- |
|
| 127 |
- va_start(ap, f); |
|
| 128 |
- vsnprintf(buffer, 256, f, ap); |
|
| 129 |
- va_end(ap); |
|
| 130 |
- |
|
| 131 |
- DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); |
|
| 132 |
-} |
|
| 133 |
- |
|
| 134 |
-static void log_with_errno_init() |
|
| 135 |
-{
|
|
| 136 |
- dm_log_with_errno_init(log_cb); |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-*/ |
|
| 140 |
-import "C" |
|
| 141 |
- |
|
| 142 |
-import ( |
|
| 143 |
- "syscall" |
|
| 144 |
- "unsafe" |
|
| 145 |
-) |
|
| 146 |
- |
|
| 147 |
-type ( |
|
| 148 |
- CDmTask C.struct_dm_task |
|
| 149 |
-) |
|
| 150 |
- |
|
| 151 |
-var ( |
|
| 152 |
- DmTaskDestory = dmTaskDestroyFct |
|
| 153 |
- DmTaskCreate = dmTaskCreateFct |
|
| 154 |
- DmTaskRun = dmTaskRunFct |
|
| 155 |
- DmTaskSetName = dmTaskSetNameFct |
|
| 156 |
- DmTaskSetMessage = dmTaskSetMessageFct |
|
| 157 |
- DmTaskSetSector = dmTaskSetSectorFct |
|
| 158 |
- DmTaskSetCookie = dmTaskSetCookieFct |
|
| 159 |
- DmTaskSetAddNode = dmTaskSetAddNodeFct |
|
| 160 |
- DmTaskSetRo = dmTaskSetRoFct |
|
| 161 |
- DmTaskAddTarget = dmTaskAddTargetFct |
|
| 162 |
- DmTaskGetInfo = dmTaskGetInfoFct |
|
| 163 |
- DmGetNextTarget = dmGetNextTargetFct |
|
| 164 |
- DmGetBlockSize = dmGetBlockSizeFct |
|
| 165 |
- DmAttachLoopDevice = dmAttachLoopDeviceFct |
|
| 166 |
- DmUdevWait = dmUdevWaitFct |
|
| 167 |
- DmLogInitVerbose = dmLogInitVerboseFct |
|
| 168 |
- DmSetDevDir = dmSetDevDirFct |
|
| 169 |
- DmGetLibraryVersion = dmGetLibraryVersionFct |
|
| 170 |
- LogWithErrnoInit = logWithErrnoInitFct |
|
| 171 |
- GetBlockSize = getBlockSizeFct |
|
| 172 |
-) |
|
| 173 |
- |
|
| 174 |
-func free(p *C.char) {
|
|
| 175 |
- C.free(unsafe.Pointer(p)) |
|
| 176 |
-} |
|
| 177 |
- |
|
| 178 |
-func dmTaskDestroyFct(task *CDmTask) {
|
|
| 179 |
- C.dm_task_destroy((*C.struct_dm_task)(task)) |
|
| 180 |
-} |
|
| 181 |
- |
|
| 182 |
-func dmTaskCreateFct(taskType int) *CDmTask {
|
|
| 183 |
- return (*CDmTask)(C.dm_task_create(C.int(taskType))) |
|
| 184 |
-} |
|
| 185 |
- |
|
| 186 |
-func dmTaskRunFct(task *CDmTask) int {
|
|
| 187 |
- return int(C.dm_task_run((*C.struct_dm_task)(task))) |
|
| 188 |
-} |
|
| 189 |
- |
|
| 190 |
-func dmTaskSetNameFct(task *CDmTask, name string) int {
|
|
| 191 |
- Cname := C.CString(name) |
|
| 192 |
- defer free(Cname) |
|
| 193 |
- |
|
| 194 |
- return int(C.dm_task_set_name((*C.struct_dm_task)(task), |
|
| 195 |
- Cname)) |
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-func dmTaskSetMessageFct(task *CDmTask, message string) int {
|
|
| 199 |
- Cmessage := C.CString(message) |
|
| 200 |
- defer free(Cmessage) |
|
| 201 |
- |
|
| 202 |
- return int(C.dm_task_set_message((*C.struct_dm_task)(task), |
|
| 203 |
- Cmessage)) |
|
| 204 |
-} |
|
| 205 |
- |
|
| 206 |
-func dmTaskSetSectorFct(task *CDmTask, sector uint64) int {
|
|
| 207 |
- return int(C.dm_task_set_sector((*C.struct_dm_task)(task), |
|
| 208 |
- C.uint64_t(sector))) |
|
| 209 |
-} |
|
| 210 |
- |
|
| 211 |
-func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 212 |
- cCookie := C.uint32_t(*cookie) |
|
| 213 |
- defer func() {
|
|
| 214 |
- *cookie = uint(cCookie) |
|
| 215 |
- }() |
|
| 216 |
- return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, |
|
| 217 |
- C.uint16_t(flags))) |
|
| 218 |
-} |
|
| 219 |
- |
|
| 220 |
-func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int {
|
|
| 221 |
- return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), |
|
| 222 |
- C.dm_add_node_t(addNode))) |
|
| 223 |
-} |
|
| 224 |
- |
|
| 225 |
-func dmTaskSetRoFct(task *CDmTask) int {
|
|
| 226 |
- return int(C.dm_task_set_ro((*C.struct_dm_task)(task))) |
|
| 227 |
-} |
|
| 228 |
- |
|
| 229 |
-func dmTaskAddTargetFct(task *CDmTask, |
|
| 230 |
- start, size uint64, ttype, params string) int {
|
|
| 231 |
- |
|
| 232 |
- Cttype := C.CString(ttype) |
|
| 233 |
- defer free(Cttype) |
|
| 234 |
- |
|
| 235 |
- Cparams := C.CString(params) |
|
| 236 |
- defer free(Cparams) |
|
| 237 |
- |
|
| 238 |
- return int(C.dm_task_add_target((*C.struct_dm_task)(task), |
|
| 239 |
- C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) |
|
| 240 |
-} |
|
| 241 |
- |
|
| 242 |
-func dmGetBlockSizeFct(fd uintptr) (int64, syscall.Errno) {
|
|
| 243 |
- var size int64 |
|
| 244 |
- _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, |
|
| 245 |
- uintptr(unsafe.Pointer(&size))) |
|
| 246 |
- return size, err |
|
| 247 |
-} |
|
| 248 |
- |
|
| 249 |
-func dmTaskGetInfoFct(task *CDmTask, info *Info) int {
|
|
| 250 |
- Cinfo := C.struct_dm_info{}
|
|
| 251 |
- defer func() {
|
|
| 252 |
- info.Exists = int(Cinfo.exists) |
|
| 253 |
- info.Suspended = int(Cinfo.suspended) |
|
| 254 |
- info.LiveTable = int(Cinfo.live_table) |
|
| 255 |
- info.InactiveTable = int(Cinfo.inactive_table) |
|
| 256 |
- info.OpenCount = int32(Cinfo.open_count) |
|
| 257 |
- info.EventNr = uint32(Cinfo.event_nr) |
|
| 258 |
- info.Major = uint32(Cinfo.major) |
|
| 259 |
- info.Minor = uint32(Cinfo.minor) |
|
| 260 |
- info.ReadOnly = int(Cinfo.read_only) |
|
| 261 |
- info.TargetCount = int32(Cinfo.target_count) |
|
| 262 |
- }() |
|
| 263 |
- return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) |
|
| 264 |
-} |
|
| 265 |
- |
|
| 266 |
-func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, |
|
| 267 |
- target, params *string) uintptr {
|
|
| 268 |
- |
|
| 269 |
- var ( |
|
| 270 |
- Cstart, Clength C.uint64_t |
|
| 271 |
- CtargetType, Cparams *C.char |
|
| 272 |
- ) |
|
| 273 |
- defer func() {
|
|
| 274 |
- *start = uint64(Cstart) |
|
| 275 |
- *length = uint64(Clength) |
|
| 276 |
- *target = C.GoString(CtargetType) |
|
| 277 |
- *params = C.GoString(Cparams) |
|
| 278 |
- }() |
|
| 279 |
- nextp := C.dm_get_next_target((*C.struct_dm_task)(task), |
|
| 280 |
- unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) |
|
| 281 |
- return uintptr(nextp) |
|
| 282 |
-} |
|
| 283 |
- |
|
| 284 |
-func dmAttachLoopDeviceFct(filename string, fd *int) string {
|
|
| 285 |
- cFilename := C.CString(filename) |
|
| 286 |
- defer free(cFilename) |
|
| 287 |
- |
|
| 288 |
- var cFd C.int |
|
| 289 |
- defer func() {
|
|
| 290 |
- *fd = int(cFd) |
|
| 291 |
- }() |
|
| 292 |
- |
|
| 293 |
- ret := C.attach_loop_device(cFilename, &cFd) |
|
| 294 |
- defer free(ret) |
|
| 295 |
- return C.GoString(ret) |
|
| 296 |
-} |
|
| 297 |
- |
|
| 298 |
-func getBlockSizeFct(fd uintptr, size *uint64) syscall.Errno {
|
|
| 299 |
- _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, |
|
| 300 |
- uintptr(unsafe.Pointer(&size))) |
|
| 301 |
- return err |
|
| 302 |
-} |
|
| 303 |
- |
|
| 304 |
-func dmUdevWaitFct(cookie uint) int {
|
|
| 305 |
- return int(C.dm_udev_wait(C.uint32_t(cookie))) |
|
| 306 |
-} |
|
| 307 |
- |
|
| 308 |
-func dmLogInitVerboseFct(level int) {
|
|
| 309 |
- C.dm_log_init_verbose(C.int(level)) |
|
| 310 |
-} |
|
| 311 |
- |
|
| 312 |
-func logWithErrnoInitFct() {
|
|
| 313 |
- C.log_with_errno_init() |
|
| 314 |
-} |
|
| 315 |
- |
|
| 316 |
-func dmSetDevDirFct(dir string) int {
|
|
| 317 |
- Cdir := C.CString(dir) |
|
| 318 |
- defer free(Cdir) |
|
| 319 |
- |
|
| 320 |
- return int(C.dm_set_dev_dir(Cdir)) |
|
| 321 |
-} |
|
| 322 |
- |
|
| 323 |
-func dmGetLibraryVersionFct(version *string) int {
|
|
| 324 |
- buffer := C.CString(string(make([]byte, 128))) |
|
| 325 |
- defer free(buffer) |
|
| 326 |
- defer func() {
|
|
| 327 |
- *version = C.GoString(buffer) |
|
| 328 |
- }() |
|
| 329 |
- return int(C.dm_get_library_version(buffer, 128)) |
|
| 330 |
-} |
| 331 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,62 +0,0 @@ |
| 1 |
-package main |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/devmapper" |
|
| 6 |
- "os" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func usage() {
|
|
| 10 |
- fmt.Printf("Usage: %s [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0])
|
|
| 11 |
- os.Exit(1) |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-func main() {
|
|
| 15 |
- devices := devmapper.NewDeviceSet("/var/lib/docker")
|
|
| 16 |
- |
|
| 17 |
- if len(os.Args) < 2 {
|
|
| 18 |
- usage() |
|
| 19 |
- } |
|
| 20 |
- |
|
| 21 |
- cmd := os.Args[1] |
|
| 22 |
- if cmd == "snap" {
|
|
| 23 |
- if len(os.Args) < 4 {
|
|
| 24 |
- usage() |
|
| 25 |
- } |
|
| 26 |
- |
|
| 27 |
- err := devices.AddDevice(os.Args[2], os.Args[3]) |
|
| 28 |
- if err != nil {
|
|
| 29 |
- fmt.Println("Can't create snap device: ", err)
|
|
| 30 |
- os.Exit(1) |
|
| 31 |
- } |
|
| 32 |
- } else if cmd == "remove" {
|
|
| 33 |
- if len(os.Args) < 3 {
|
|
| 34 |
- usage() |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- err := devices.RemoveDevice(os.Args[2]) |
|
| 38 |
- if err != nil {
|
|
| 39 |
- fmt.Println("Can't remove device: ", err)
|
|
| 40 |
- os.Exit(1) |
|
| 41 |
- } |
|
| 42 |
- } else if cmd == "mount" {
|
|
| 43 |
- if len(os.Args) < 4 {
|
|
| 44 |
- usage() |
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- err := devices.MountDevice(os.Args[2], os.Args[3]) |
|
| 48 |
- if err != nil {
|
|
| 49 |
- fmt.Println("Can't create snap device: ", err)
|
|
| 50 |
- os.Exit(1) |
|
| 51 |
- } |
|
| 52 |
- } else {
|
|
| 53 |
- fmt.Printf("Unknown command %s\n", cmd)
|
|
| 54 |
- if len(os.Args) < 4 {
|
|
| 55 |
- usage() |
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- os.Exit(1) |
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- return |
|
| 62 |
-} |
| 63 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,92 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 6 |
- "os" |
|
| 7 |
- "path" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func init() {
|
|
| 11 |
- graphdriver.Register("devicemapper", Init)
|
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-// Placeholder interfaces, to be replaced |
|
| 15 |
-// at integration. |
|
| 16 |
- |
|
| 17 |
-// End of placeholder interfaces. |
|
| 18 |
- |
|
| 19 |
-type Driver struct {
|
|
| 20 |
- *DeviceSet |
|
| 21 |
- home string |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-func Init(home string) (graphdriver.Driver, error) {
|
|
| 25 |
- deviceSet, err := NewDeviceSet(home) |
|
| 26 |
- if err != nil {
|
|
| 27 |
- return nil, err |
|
| 28 |
- } |
|
| 29 |
- d := &Driver{
|
|
| 30 |
- DeviceSet: deviceSet, |
|
| 31 |
- home: home, |
|
| 32 |
- } |
|
| 33 |
- return d, nil |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-func (d *Driver) String() string {
|
|
| 37 |
- return "devicemapper" |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (d *Driver) Status() [][2]string {
|
|
| 41 |
- s := d.DeviceSet.Status() |
|
| 42 |
- |
|
| 43 |
- status := [][2]string{
|
|
| 44 |
- {"Pool Name", s.PoolName},
|
|
| 45 |
- {"Data file", s.DataLoopback},
|
|
| 46 |
- {"Metadata file", s.MetadataLoopback},
|
|
| 47 |
- {"Data Space Used", fmt.Sprintf("%.1f Mb", float64(s.Data.Used)/(1024*1024))},
|
|
| 48 |
- {"Data Space Total", fmt.Sprintf("%.1f Mb", float64(s.Data.Total)/(1024*1024))},
|
|
| 49 |
- {"Metadata Space Used", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Used)/(1024*1024))},
|
|
| 50 |
- {"Metadata Space Total", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Total)/(1024*1024))},
|
|
| 51 |
- } |
|
| 52 |
- return status |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-func (d *Driver) Cleanup() error {
|
|
| 56 |
- return d.DeviceSet.Shutdown() |
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-func (d *Driver) Create(id string, parent string) error {
|
|
| 60 |
- return d.DeviceSet.AddDevice(id, parent) |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-func (d *Driver) Remove(id string) error {
|
|
| 64 |
- return d.DeviceSet.RemoveDevice(id) |
|
| 65 |
-} |
|
| 66 |
- |
|
| 67 |
-func (d *Driver) Get(id string) (string, error) {
|
|
| 68 |
- mp := path.Join(d.home, "mnt", id) |
|
| 69 |
- if err := d.mount(id, mp); err != nil {
|
|
| 70 |
- return "", err |
|
| 71 |
- } |
|
| 72 |
- return mp, nil |
|
| 73 |
-} |
|
| 74 |
- |
|
| 75 |
-func (d *Driver) Size(id string) (int64, error) {
|
|
| 76 |
- return -1, fmt.Errorf("Not implemented")
|
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-func (d *Driver) mount(id, mountPoint string) error {
|
|
| 80 |
- // Create the target directories if they don't exist |
|
| 81 |
- if err := os.MkdirAll(mountPoint, 0755); err != nil && !os.IsExist(err) {
|
|
| 82 |
- return err |
|
| 83 |
- } |
|
| 84 |
- // If mountpoint is already mounted, do nothing |
|
| 85 |
- if mounted, err := Mounted(mountPoint); err != nil {
|
|
| 86 |
- return fmt.Errorf("Error checking mountpoint: %s", err)
|
|
| 87 |
- } else if mounted {
|
|
| 88 |
- return nil |
|
| 89 |
- } |
|
| 90 |
- // Mount the device |
|
| 91 |
- return d.DeviceSet.MountDevice(id, mountPoint, false) |
|
| 92 |
-} |
| 93 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,301 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "io/ioutil" |
|
| 5 |
- "os" |
|
| 6 |
- "path" |
|
| 7 |
- "testing" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func init() {
|
|
| 11 |
- // Reduce the size the the base fs and loopback for the tests |
|
| 12 |
- DefaultDataLoopbackSize = 300 * 1024 * 1024 |
|
| 13 |
- DefaultMetaDataLoopbackSize = 200 * 1024 * 1024 |
|
| 14 |
- DefaultBaseFsSize = 300 * 1024 * 1024 |
|
| 15 |
- |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-func mkTestDirectory(t *testing.T) string {
|
|
| 19 |
- dir, err := ioutil.TempDir("", "docker-test-devmapper-")
|
|
| 20 |
- if err != nil {
|
|
| 21 |
- t.Fatal(err) |
|
| 22 |
- } |
|
| 23 |
- return dir |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-func newDriver(t *testing.T) *Driver {
|
|
| 27 |
- home := mkTestDirectory(t) |
|
| 28 |
- d, err := Init(home) |
|
| 29 |
- if err != nil {
|
|
| 30 |
- t.Fatal(err) |
|
| 31 |
- } |
|
| 32 |
- return d.(*Driver) |
|
| 33 |
-} |
|
| 34 |
- |
|
| 35 |
-func cleanup(d *Driver) {
|
|
| 36 |
- d.Cleanup() |
|
| 37 |
- os.RemoveAll(d.home) |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func TestInit(t *testing.T) {
|
|
| 41 |
- home := mkTestDirectory(t) |
|
| 42 |
- defer os.RemoveAll(home) |
|
| 43 |
- driver, err := Init(home) |
|
| 44 |
- if err != nil {
|
|
| 45 |
- t.Fatal(err) |
|
| 46 |
- } |
|
| 47 |
- defer func() {
|
|
| 48 |
- if err := driver.Cleanup(); err != nil {
|
|
| 49 |
- t.Fatal(err) |
|
| 50 |
- } |
|
| 51 |
- }() |
|
| 52 |
- |
|
| 53 |
- id := "foo" |
|
| 54 |
- if err := driver.Create(id, ""); err != nil {
|
|
| 55 |
- t.Fatal(err) |
|
| 56 |
- } |
|
| 57 |
- dir, err := driver.Get(id) |
|
| 58 |
- if err != nil {
|
|
| 59 |
- t.Fatal(err) |
|
| 60 |
- } |
|
| 61 |
- if st, err := os.Stat(dir); err != nil {
|
|
| 62 |
- t.Fatal(err) |
|
| 63 |
- } else if !st.IsDir() {
|
|
| 64 |
- t.Fatalf("Get(%V) did not return a directory", id)
|
|
| 65 |
- } |
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-func TestDriverName(t *testing.T) {
|
|
| 69 |
- d := newDriver(t) |
|
| 70 |
- defer cleanup(d) |
|
| 71 |
- |
|
| 72 |
- if d.String() != "devicemapper" {
|
|
| 73 |
- t.Fatalf("Expected driver name to be devicemapper got %s", d.String())
|
|
| 74 |
- } |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-func TestDriverCreate(t *testing.T) {
|
|
| 78 |
- d := newDriver(t) |
|
| 79 |
- defer cleanup(d) |
|
| 80 |
- |
|
| 81 |
- if err := d.Create("1", ""); err != nil {
|
|
| 82 |
- t.Fatal(err) |
|
| 83 |
- } |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-func TestDriverRemove(t *testing.T) {
|
|
| 87 |
- d := newDriver(t) |
|
| 88 |
- defer cleanup(d) |
|
| 89 |
- |
|
| 90 |
- if err := d.Create("1", ""); err != nil {
|
|
| 91 |
- t.Fatal(err) |
|
| 92 |
- } |
|
| 93 |
- |
|
| 94 |
- if err := d.Remove("1"); err != nil {
|
|
| 95 |
- t.Fatal(err) |
|
| 96 |
- } |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-func TestCleanup(t *testing.T) {
|
|
| 100 |
- d := newDriver(t) |
|
| 101 |
- defer os.RemoveAll(d.home) |
|
| 102 |
- |
|
| 103 |
- mountPoints := make([]string, 2) |
|
| 104 |
- |
|
| 105 |
- if err := d.Create("1", ""); err != nil {
|
|
| 106 |
- t.Fatal(err) |
|
| 107 |
- } |
|
| 108 |
- // Mount the id |
|
| 109 |
- p, err := d.Get("1")
|
|
| 110 |
- if err != nil {
|
|
| 111 |
- t.Fatal(err) |
|
| 112 |
- } |
|
| 113 |
- mountPoints[0] = p |
|
| 114 |
- |
|
| 115 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 116 |
- t.Fatal(err) |
|
| 117 |
- } |
|
| 118 |
- |
|
| 119 |
- p, err = d.Get("2")
|
|
| 120 |
- if err != nil {
|
|
| 121 |
- t.Fatal(err) |
|
| 122 |
- } |
|
| 123 |
- mountPoints[1] = p |
|
| 124 |
- |
|
| 125 |
- // Ensure that all the mount points are currently mounted |
|
| 126 |
- for _, p := range mountPoints {
|
|
| 127 |
- if mounted, err := Mounted(p); err != nil {
|
|
| 128 |
- t.Fatal(err) |
|
| 129 |
- } else if !mounted {
|
|
| 130 |
- t.Fatalf("Expected %s to be mounted", p)
|
|
| 131 |
- } |
|
| 132 |
- } |
|
| 133 |
- |
|
| 134 |
- // Ensure that devices are active |
|
| 135 |
- for _, p := range []string{"1", "2"} {
|
|
| 136 |
- if !d.HasActivatedDevice(p) {
|
|
| 137 |
- t.Fatalf("Expected %s to have an active device", p)
|
|
| 138 |
- } |
|
| 139 |
- } |
|
| 140 |
- |
|
| 141 |
- if err := d.Cleanup(); err != nil {
|
|
| 142 |
- t.Fatal(err) |
|
| 143 |
- } |
|
| 144 |
- |
|
| 145 |
- // Ensure that all the mount points are no longer mounted |
|
| 146 |
- for _, p := range mountPoints {
|
|
| 147 |
- if mounted, err := Mounted(p); err != nil {
|
|
| 148 |
- t.Fatal(err) |
|
| 149 |
- } else if mounted {
|
|
| 150 |
- t.Fatalf("Expected %s to not be mounted", p)
|
|
| 151 |
- } |
|
| 152 |
- } |
|
| 153 |
- |
|
| 154 |
- // Ensure that devices are no longer activated |
|
| 155 |
- for _, p := range []string{"1", "2"} {
|
|
| 156 |
- if d.HasActivatedDevice(p) {
|
|
| 157 |
- t.Fatalf("Expected %s not be an active device", p)
|
|
| 158 |
- } |
|
| 159 |
- } |
|
| 160 |
-} |
|
| 161 |
- |
|
| 162 |
-func TestNotMounted(t *testing.T) {
|
|
| 163 |
- d := newDriver(t) |
|
| 164 |
- defer cleanup(d) |
|
| 165 |
- |
|
| 166 |
- if err := d.Create("1", ""); err != nil {
|
|
| 167 |
- t.Fatal(err) |
|
| 168 |
- } |
|
| 169 |
- |
|
| 170 |
- mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 171 |
- if err != nil {
|
|
| 172 |
- t.Fatal(err) |
|
| 173 |
- } |
|
| 174 |
- if mounted {
|
|
| 175 |
- t.Fatal("Id 1 should not be mounted")
|
|
| 176 |
- } |
|
| 177 |
-} |
|
| 178 |
- |
|
| 179 |
-func TestMounted(t *testing.T) {
|
|
| 180 |
- d := newDriver(t) |
|
| 181 |
- defer cleanup(d) |
|
| 182 |
- |
|
| 183 |
- if err := d.Create("1", ""); err != nil {
|
|
| 184 |
- t.Fatal(err) |
|
| 185 |
- } |
|
| 186 |
- if _, err := d.Get("1"); err != nil {
|
|
| 187 |
- t.Fatal(err) |
|
| 188 |
- } |
|
| 189 |
- |
|
| 190 |
- mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 191 |
- if err != nil {
|
|
| 192 |
- t.Fatal(err) |
|
| 193 |
- } |
|
| 194 |
- if !mounted {
|
|
| 195 |
- t.Fatal("Id 1 should be mounted")
|
|
| 196 |
- } |
|
| 197 |
-} |
|
| 198 |
- |
|
| 199 |
-func TestInitCleanedDriver(t *testing.T) {
|
|
| 200 |
- d := newDriver(t) |
|
| 201 |
- |
|
| 202 |
- if err := d.Create("1", ""); err != nil {
|
|
| 203 |
- t.Fatal(err) |
|
| 204 |
- } |
|
| 205 |
- if _, err := d.Get("1"); err != nil {
|
|
| 206 |
- t.Fatal(err) |
|
| 207 |
- } |
|
| 208 |
- |
|
| 209 |
- if err := d.Cleanup(); err != nil {
|
|
| 210 |
- t.Fatal(err) |
|
| 211 |
- } |
|
| 212 |
- |
|
| 213 |
- driver, err := Init(d.home) |
|
| 214 |
- if err != nil {
|
|
| 215 |
- t.Fatal(err) |
|
| 216 |
- } |
|
| 217 |
- d = driver.(*Driver) |
|
| 218 |
- defer cleanup(d) |
|
| 219 |
- |
|
| 220 |
- if _, err := d.Get("1"); err != nil {
|
|
| 221 |
- t.Fatal(err) |
|
| 222 |
- } |
|
| 223 |
-} |
|
| 224 |
- |
|
| 225 |
-func TestMountMountedDriver(t *testing.T) {
|
|
| 226 |
- d := newDriver(t) |
|
| 227 |
- defer cleanup(d) |
|
| 228 |
- |
|
| 229 |
- if err := d.Create("1", ""); err != nil {
|
|
| 230 |
- t.Fatal(err) |
|
| 231 |
- } |
|
| 232 |
- |
|
| 233 |
- // Perform get on same id to ensure that it will |
|
| 234 |
- // not be mounted twice |
|
| 235 |
- if _, err := d.Get("1"); err != nil {
|
|
| 236 |
- t.Fatal(err) |
|
| 237 |
- } |
|
| 238 |
- if _, err := d.Get("1"); err != nil {
|
|
| 239 |
- t.Fatal(err) |
|
| 240 |
- } |
|
| 241 |
-} |
|
| 242 |
- |
|
| 243 |
-func TestGetReturnsValidDevice(t *testing.T) {
|
|
| 244 |
- d := newDriver(t) |
|
| 245 |
- defer cleanup(d) |
|
| 246 |
- |
|
| 247 |
- if err := d.Create("1", ""); err != nil {
|
|
| 248 |
- t.Fatal(err) |
|
| 249 |
- } |
|
| 250 |
- |
|
| 251 |
- if !d.HasDevice("1") {
|
|
| 252 |
- t.Fatalf("Expected id 1 to be in device set")
|
|
| 253 |
- } |
|
| 254 |
- |
|
| 255 |
- if _, err := d.Get("1"); err != nil {
|
|
| 256 |
- t.Fatal(err) |
|
| 257 |
- } |
|
| 258 |
- |
|
| 259 |
- if !d.HasActivatedDevice("1") {
|
|
| 260 |
- t.Fatalf("Expected id 1 to be activated")
|
|
| 261 |
- } |
|
| 262 |
- |
|
| 263 |
- if !d.HasInitializedDevice("1") {
|
|
| 264 |
- t.Fatalf("Expected id 1 to be initialized")
|
|
| 265 |
- } |
|
| 266 |
-} |
|
| 267 |
- |
|
| 268 |
-func TestDriverGetSize(t *testing.T) {
|
|
| 269 |
- t.Skipf("Size is currently not implemented")
|
|
| 270 |
- |
|
| 271 |
- d := newDriver(t) |
|
| 272 |
- defer cleanup(d) |
|
| 273 |
- |
|
| 274 |
- if err := d.Create("1", ""); err != nil {
|
|
| 275 |
- t.Fatal(err) |
|
| 276 |
- } |
|
| 277 |
- |
|
| 278 |
- mountPoint, err := d.Get("1")
|
|
| 279 |
- if err != nil {
|
|
| 280 |
- t.Fatal(err) |
|
| 281 |
- } |
|
| 282 |
- |
|
| 283 |
- size := int64(1024) |
|
| 284 |
- |
|
| 285 |
- f, err := os.Create(path.Join(mountPoint, "test_file")) |
|
| 286 |
- if err != nil {
|
|
| 287 |
- t.Fatal(err) |
|
| 288 |
- } |
|
| 289 |
- if err := f.Truncate(size); err != nil {
|
|
| 290 |
- t.Fatal(err) |
|
| 291 |
- } |
|
| 292 |
- f.Close() |
|
| 293 |
- |
|
| 294 |
- diffSize, err := d.Size("1")
|
|
| 295 |
- if err != nil {
|
|
| 296 |
- t.Fatal(err) |
|
| 297 |
- } |
|
| 298 |
- if diffSize != size {
|
|
| 299 |
- t.Fatalf("Expected size %d got %d", size, diffSize)
|
|
| 300 |
- } |
|
| 301 |
-} |
| 302 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,27 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "os" |
|
| 5 |
- "path/filepath" |
|
| 6 |
- "syscall" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-// FIXME: this is copy-pasted from the aufs driver. |
|
| 10 |
-// It should be moved into the core. |
|
| 11 |
- |
|
| 12 |
-func Mounted(mountpoint string) (bool, error) {
|
|
| 13 |
- mntpoint, err := os.Stat(mountpoint) |
|
| 14 |
- if err != nil {
|
|
| 15 |
- if os.IsNotExist(err) {
|
|
| 16 |
- return false, nil |
|
| 17 |
- } |
|
| 18 |
- return false, err |
|
| 19 |
- } |
|
| 20 |
- parent, err := os.Stat(filepath.Join(mountpoint, "..")) |
|
| 21 |
- if err != nil {
|
|
| 22 |
- return false, err |
|
| 23 |
- } |
|
| 24 |
- mntpointSt := mntpoint.Sys().(*syscall.Stat_t) |
|
| 25 |
- parentSt := parent.Sys().(*syscall.Stat_t) |
|
| 26 |
- return mntpointSt.Dev != parentSt.Dev, nil |
|
| 27 |
-} |
| 28 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,320 @@ |
| 0 |
+/* |
|
| 1 |
+ |
|
| 2 |
+aufs driver directory structure |
|
| 3 |
+ |
|
| 4 |
+. |
|
| 5 |
+├── layers // Metadata of layers |
|
| 6 |
+│ ├── 1 |
|
| 7 |
+│ ├── 2 |
|
| 8 |
+│ └── 3 |
|
| 9 |
+├── diffs // Content of the layer |
|
| 10 |
+│ ├── 1 // Contains layers that need to be mounted for the id |
|
| 11 |
+│ ├── 2 |
|
| 12 |
+│ └── 3 |
|
| 13 |
+└── mnt // Mount points for the rw layers to be mounted |
|
| 14 |
+ ├── 1 |
|
| 15 |
+ ├── 2 |
|
| 16 |
+ └── 3 |
|
| 17 |
+ |
|
| 18 |
+*/ |
|
| 19 |
+ |
|
| 20 |
+package aufs |
|
| 21 |
+ |
|
| 22 |
+import ( |
|
| 23 |
+ "bufio" |
|
| 24 |
+ "fmt" |
|
| 25 |
+ "github.com/dotcloud/docker/archive" |
|
| 26 |
+ "github.com/dotcloud/docker/graphdriver" |
|
| 27 |
+ "github.com/dotcloud/docker/utils" |
|
| 28 |
+ "log" |
|
| 29 |
+ "os" |
|
| 30 |
+ "os/exec" |
|
| 31 |
+ "path" |
|
| 32 |
+ "strings" |
|
| 33 |
+) |
|
| 34 |
+ |
|
| 35 |
+func init() {
|
|
| 36 |
+ graphdriver.Register("aufs", Init)
|
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+type AufsDriver struct {
|
|
| 40 |
+ root string |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// New returns a new AUFS driver. |
|
| 44 |
+// An error is returned if AUFS is not supported. |
|
| 45 |
+func Init(root string) (graphdriver.Driver, error) {
|
|
| 46 |
+ // Try to load the aufs kernel module |
|
| 47 |
+ if err := supportsAufs(); err != nil {
|
|
| 48 |
+ return nil, err |
|
| 49 |
+ } |
|
| 50 |
+ paths := []string{
|
|
| 51 |
+ "mnt", |
|
| 52 |
+ "diff", |
|
| 53 |
+ "layers", |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ // Create the root aufs driver dir and return |
|
| 57 |
+ // if it already exists |
|
| 58 |
+ // If not populate the dir structure |
|
| 59 |
+ if err := os.MkdirAll(root, 0755); err != nil {
|
|
| 60 |
+ if os.IsExist(err) {
|
|
| 61 |
+ return &AufsDriver{root}, nil
|
|
| 62 |
+ } |
|
| 63 |
+ return nil, err |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ for _, p := range paths {
|
|
| 67 |
+ if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
|
|
| 68 |
+ return nil, err |
|
| 69 |
+ } |
|
| 70 |
+ } |
|
| 71 |
+ return &AufsDriver{root}, nil
|
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+// Return a nil error if the kernel supports aufs |
|
| 75 |
+// We cannot modprobe because inside dind modprobe fails |
|
| 76 |
+// to run |
|
| 77 |
+func supportsAufs() error {
|
|
| 78 |
+ // We can try to modprobe aufs first before looking at |
|
| 79 |
+ // proc/filesystems for when aufs is supported |
|
| 80 |
+ exec.Command("modprobe", "aufs").Run()
|
|
| 81 |
+ |
|
| 82 |
+ f, err := os.Open("/proc/filesystems")
|
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return err |
|
| 85 |
+ } |
|
| 86 |
+ defer f.Close() |
|
| 87 |
+ |
|
| 88 |
+ s := bufio.NewScanner(f) |
|
| 89 |
+ for s.Scan() {
|
|
| 90 |
+ if strings.Contains(s.Text(), "aufs") {
|
|
| 91 |
+ return nil |
|
| 92 |
+ } |
|
| 93 |
+ } |
|
| 94 |
+ return fmt.Errorf("AUFS was not found in /proc/filesystems")
|
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func (a *AufsDriver) rootPath() string {
|
|
| 98 |
+ return a.root |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+func (a *AufsDriver) String() string {
|
|
| 102 |
+ return "aufs" |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+func (d *AufsDriver) Status() [][2]string {
|
|
| 106 |
+ return nil |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// Three folders are created for each id |
|
| 110 |
+// mnt, layers, and diff |
|
| 111 |
+func (a *AufsDriver) Create(id, parent string) error {
|
|
| 112 |
+ if err := a.createDirsFor(id); err != nil {
|
|
| 113 |
+ return err |
|
| 114 |
+ } |
|
| 115 |
+ // Write the layers metadata |
|
| 116 |
+ f, err := os.Create(path.Join(a.rootPath(), "layers", id)) |
|
| 117 |
+ if err != nil {
|
|
| 118 |
+ return err |
|
| 119 |
+ } |
|
| 120 |
+ defer f.Close() |
|
| 121 |
+ |
|
| 122 |
+ if parent != "" {
|
|
| 123 |
+ ids, err := getParentIds(a.rootPath(), parent) |
|
| 124 |
+ if err != nil {
|
|
| 125 |
+ return err |
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ if _, err := fmt.Fprintln(f, parent); err != nil {
|
|
| 129 |
+ return err |
|
| 130 |
+ } |
|
| 131 |
+ for _, i := range ids {
|
|
| 132 |
+ if _, err := fmt.Fprintln(f, i); err != nil {
|
|
| 133 |
+ return err |
|
| 134 |
+ } |
|
| 135 |
+ } |
|
| 136 |
+ } |
|
| 137 |
+ return nil |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+func (a *AufsDriver) createDirsFor(id string) error {
|
|
| 141 |
+ paths := []string{
|
|
| 142 |
+ "mnt", |
|
| 143 |
+ "diff", |
|
| 144 |
+ } |
|
| 145 |
+ |
|
| 146 |
+ for _, p := range paths {
|
|
| 147 |
+ if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
|
|
| 148 |
+ return err |
|
| 149 |
+ } |
|
| 150 |
+ } |
|
| 151 |
+ return nil |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+// Unmount and remove the dir information |
|
| 155 |
+func (a *AufsDriver) Remove(id string) error {
|
|
| 156 |
+ // Make sure the dir is umounted first |
|
| 157 |
+ if err := a.unmount(id); err != nil {
|
|
| 158 |
+ return err |
|
| 159 |
+ } |
|
| 160 |
+ tmpDirs := []string{
|
|
| 161 |
+ "mnt", |
|
| 162 |
+ "diff", |
|
| 163 |
+ } |
|
| 164 |
+ |
|
| 165 |
+ // Remove the dirs atomically |
|
| 166 |
+ for _, p := range tmpDirs {
|
|
| 167 |
+ // We need to use a temp dir in the same dir as the driver so Rename |
|
| 168 |
+ // does not fall back to the slow copy if /tmp and the driver dir |
|
| 169 |
+ // are on different devices |
|
| 170 |
+ tmp := path.Join(a.rootPath(), "tmp", p, id) |
|
| 171 |
+ if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 172 |
+ return err |
|
| 173 |
+ } |
|
| 174 |
+ realPath := path.Join(a.rootPath(), p, id) |
|
| 175 |
+ if err := os.Rename(realPath, tmp); err != nil {
|
|
| 176 |
+ return err |
|
| 177 |
+ } |
|
| 178 |
+ defer os.RemoveAll(tmp) |
|
| 179 |
+ } |
|
| 180 |
+ |
|
| 181 |
+ // Remove the layers file for the id |
|
| 182 |
+ return os.Remove(path.Join(a.rootPath(), "layers", id)) |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+// Return the rootfs path for the id |
|
| 186 |
+// This will mount the dir at it's given path |
|
| 187 |
+func (a *AufsDriver) Get(id string) (string, error) {
|
|
| 188 |
+ ids, err := getParentIds(a.rootPath(), id) |
|
| 189 |
+ if err != nil {
|
|
| 190 |
+ if !os.IsNotExist(err) {
|
|
| 191 |
+ return "", err |
|
| 192 |
+ } |
|
| 193 |
+ ids = []string{}
|
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ // If a dir does not have a parent ( no layers )do not try to mount |
|
| 197 |
+ // just return the diff path to the data |
|
| 198 |
+ out := path.Join(a.rootPath(), "diff", id) |
|
| 199 |
+ if len(ids) > 0 {
|
|
| 200 |
+ out = path.Join(a.rootPath(), "mnt", id) |
|
| 201 |
+ if err := a.mount(id); err != nil {
|
|
| 202 |
+ return "", err |
|
| 203 |
+ } |
|
| 204 |
+ } |
|
| 205 |
+ return out, nil |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+// Returns an archive of the contents for the id |
|
| 209 |
+func (a *AufsDriver) Diff(id string) (archive.Archive, error) {
|
|
| 210 |
+ return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
|
| 211 |
+ Recursive: true, |
|
| 212 |
+ Compression: archive.Uncompressed, |
|
| 213 |
+ }) |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 216 |
+func (a *AufsDriver) ApplyDiff(id string, diff archive.Archive) error {
|
|
| 217 |
+ return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil) |
|
| 218 |
+} |
|
| 219 |
+ |
|
| 220 |
+// Returns the size of the contents for the id |
|
| 221 |
+func (a *AufsDriver) Size(id string) (int64, error) {
|
|
| 222 |
+ return utils.TreeSize(path.Join(a.rootPath(), "diff", id)) |
|
| 223 |
+} |
|
| 224 |
+ |
|
| 225 |
+func (a *AufsDriver) Changes(id string) ([]archive.Change, error) {
|
|
| 226 |
+ layers, err := a.getParentLayerPaths(id) |
|
| 227 |
+ if err != nil {
|
|
| 228 |
+ return nil, err |
|
| 229 |
+ } |
|
| 230 |
+ return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) |
|
| 231 |
+} |
|
| 232 |
+ |
|
| 233 |
+func (a *AufsDriver) getParentLayerPaths(id string) ([]string, error) {
|
|
| 234 |
+ parentIds, err := getParentIds(a.rootPath(), id) |
|
| 235 |
+ if err != nil {
|
|
| 236 |
+ return nil, err |
|
| 237 |
+ } |
|
| 238 |
+ if len(parentIds) == 0 {
|
|
| 239 |
+ return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
|
|
| 240 |
+ } |
|
| 241 |
+ layers := make([]string, len(parentIds)) |
|
| 242 |
+ |
|
| 243 |
+ // Get the diff paths for all the parent ids |
|
| 244 |
+ for i, p := range parentIds {
|
|
| 245 |
+ layers[i] = path.Join(a.rootPath(), "diff", p) |
|
| 246 |
+ } |
|
| 247 |
+ return layers, nil |
|
| 248 |
+} |
|
| 249 |
+ |
|
| 250 |
+func (a *AufsDriver) mount(id string) error {
|
|
| 251 |
+ // If the id is mounted or we get an error return |
|
| 252 |
+ if mounted, err := a.mounted(id); err != nil || mounted {
|
|
| 253 |
+ return err |
|
| 254 |
+ } |
|
| 255 |
+ |
|
| 256 |
+ var ( |
|
| 257 |
+ target = path.Join(a.rootPath(), "mnt", id) |
|
| 258 |
+ rw = path.Join(a.rootPath(), "diff", id) |
|
| 259 |
+ ) |
|
| 260 |
+ |
|
| 261 |
+ layers, err := a.getParentLayerPaths(id) |
|
| 262 |
+ if err != nil {
|
|
| 263 |
+ return err |
|
| 264 |
+ } |
|
| 265 |
+ |
|
| 266 |
+ if err := a.aufsMount(layers, rw, target); err != nil {
|
|
| 267 |
+ return err |
|
| 268 |
+ } |
|
| 269 |
+ return nil |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+func (a *AufsDriver) unmount(id string) error {
|
|
| 273 |
+ if mounted, err := a.mounted(id); err != nil || !mounted {
|
|
| 274 |
+ return err |
|
| 275 |
+ } |
|
| 276 |
+ target := path.Join(a.rootPath(), "mnt", id) |
|
| 277 |
+ return Unmount(target) |
|
| 278 |
+} |
|
| 279 |
+ |
|
| 280 |
+func (a *AufsDriver) mounted(id string) (bool, error) {
|
|
| 281 |
+ target := path.Join(a.rootPath(), "mnt", id) |
|
| 282 |
+ return Mounted(target) |
|
| 283 |
+} |
|
| 284 |
+ |
|
| 285 |
+// During cleanup aufs needs to unmount all mountpoints |
|
| 286 |
+func (a *AufsDriver) Cleanup() error {
|
|
| 287 |
+ ids, err := loadIds(path.Join(a.rootPath(), "layers")) |
|
| 288 |
+ if err != nil {
|
|
| 289 |
+ return err |
|
| 290 |
+ } |
|
| 291 |
+ for _, id := range ids {
|
|
| 292 |
+ if err := a.unmount(id); err != nil {
|
|
| 293 |
+ return err |
|
| 294 |
+ } |
|
| 295 |
+ } |
|
| 296 |
+ return nil |
|
| 297 |
+} |
|
| 298 |
+ |
|
| 299 |
+func (a *AufsDriver) aufsMount(ro []string, rw, target string) error {
|
|
| 300 |
+ rwBranch := fmt.Sprintf("%v=rw", rw)
|
|
| 301 |
+ roBranches := "" |
|
| 302 |
+ for _, layer := range ro {
|
|
| 303 |
+ roBranches += fmt.Sprintf("%v=ro+wh:", layer)
|
|
| 304 |
+ } |
|
| 305 |
+ branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches)
|
|
| 306 |
+ |
|
| 307 |
+ //if error, try to load aufs kernel module |
|
| 308 |
+ if err := mount("none", target, "aufs", 0, branches); err != nil {
|
|
| 309 |
+ log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
|
|
| 310 |
+ if err := exec.Command("modprobe", "aufs").Run(); err != nil {
|
|
| 311 |
+ return fmt.Errorf("Unable to load the AUFS module")
|
|
| 312 |
+ } |
|
| 313 |
+ log.Printf("...module loaded.")
|
|
| 314 |
+ if err := mount("none", target, "aufs", 0, branches); err != nil {
|
|
| 315 |
+ return fmt.Errorf("Unable to mount using aufs %s", err)
|
|
| 316 |
+ } |
|
| 317 |
+ } |
|
| 318 |
+ return nil |
|
| 319 |
+} |
| 0 | 320 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,517 @@ |
| 0 |
+package aufs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/archive" |
|
| 4 |
+ "os" |
|
| 5 |
+ "path" |
|
| 6 |
+ "testing" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var ( |
|
| 10 |
+ tmp = path.Join(os.TempDir(), "aufs-tests", "aufs") |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func newDriver(t *testing.T) *AufsDriver {
|
|
| 14 |
+ if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 15 |
+ t.Fatal(err) |
|
| 16 |
+ } |
|
| 17 |
+ |
|
| 18 |
+ d, err := Init(tmp) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ t.Fatal(err) |
|
| 21 |
+ } |
|
| 22 |
+ return d.(*AufsDriver) |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func TestNewAufsDriver(t *testing.T) {
|
|
| 26 |
+ if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 27 |
+ t.Fatal(err) |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ d, err := Init(tmp) |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ t.Fatal(err) |
|
| 33 |
+ } |
|
| 34 |
+ defer os.RemoveAll(tmp) |
|
| 35 |
+ if d == nil {
|
|
| 36 |
+ t.Fatalf("Driver should not be nil")
|
|
| 37 |
+ } |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+func TestAufsString(t *testing.T) {
|
|
| 41 |
+ d := newDriver(t) |
|
| 42 |
+ defer os.RemoveAll(tmp) |
|
| 43 |
+ |
|
| 44 |
+ if d.String() != "aufs" {
|
|
| 45 |
+ t.Fatalf("Expected aufs got %s", d.String())
|
|
| 46 |
+ } |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func TestCreateDirStructure(t *testing.T) {
|
|
| 50 |
+ newDriver(t) |
|
| 51 |
+ defer os.RemoveAll(tmp) |
|
| 52 |
+ |
|
| 53 |
+ paths := []string{
|
|
| 54 |
+ "mnt", |
|
| 55 |
+ "layers", |
|
| 56 |
+ "diff", |
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ for _, p := range paths {
|
|
| 60 |
+ if _, err := os.Stat(path.Join(tmp, p)); err != nil {
|
|
| 61 |
+ t.Fatal(err) |
|
| 62 |
+ } |
|
| 63 |
+ } |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+// We should be able to create two drivers with the same dir structure |
|
| 67 |
+func TestNewDriverFromExistingDir(t *testing.T) {
|
|
| 68 |
+ if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 69 |
+ t.Fatal(err) |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ if _, err := Init(tmp); err != nil {
|
|
| 73 |
+ t.Fatal(err) |
|
| 74 |
+ } |
|
| 75 |
+ if _, err := Init(tmp); err != nil {
|
|
| 76 |
+ t.Fatal(err) |
|
| 77 |
+ } |
|
| 78 |
+ os.RemoveAll(tmp) |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func TestCreateNewDir(t *testing.T) {
|
|
| 82 |
+ d := newDriver(t) |
|
| 83 |
+ defer os.RemoveAll(tmp) |
|
| 84 |
+ |
|
| 85 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 86 |
+ t.Fatal(err) |
|
| 87 |
+ } |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func TestCreateNewDirStructure(t *testing.T) {
|
|
| 91 |
+ d := newDriver(t) |
|
| 92 |
+ defer os.RemoveAll(tmp) |
|
| 93 |
+ |
|
| 94 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 95 |
+ t.Fatal(err) |
|
| 96 |
+ } |
|
| 97 |
+ |
|
| 98 |
+ paths := []string{
|
|
| 99 |
+ "mnt", |
|
| 100 |
+ "diff", |
|
| 101 |
+ "layers", |
|
| 102 |
+ } |
|
| 103 |
+ |
|
| 104 |
+ for _, p := range paths {
|
|
| 105 |
+ if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
|
|
| 106 |
+ t.Fatal(err) |
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+func TestRemoveImage(t *testing.T) {
|
|
| 112 |
+ d := newDriver(t) |
|
| 113 |
+ defer os.RemoveAll(tmp) |
|
| 114 |
+ |
|
| 115 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 116 |
+ t.Fatal(err) |
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 119 |
+ if err := d.Remove("1"); err != nil {
|
|
| 120 |
+ t.Fatal(err) |
|
| 121 |
+ } |
|
| 122 |
+ |
|
| 123 |
+ paths := []string{
|
|
| 124 |
+ "mnt", |
|
| 125 |
+ "diff", |
|
| 126 |
+ "layers", |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ for _, p := range paths {
|
|
| 130 |
+ if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
|
|
| 131 |
+ t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
|
|
| 132 |
+ } |
|
| 133 |
+ } |
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+func TestGetWithoutParent(t *testing.T) {
|
|
| 137 |
+ d := newDriver(t) |
|
| 138 |
+ defer os.RemoveAll(tmp) |
|
| 139 |
+ |
|
| 140 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 141 |
+ t.Fatal(err) |
|
| 142 |
+ } |
|
| 143 |
+ |
|
| 144 |
+ diffPath, err := d.Get("1")
|
|
| 145 |
+ if err != nil {
|
|
| 146 |
+ t.Fatal(err) |
|
| 147 |
+ } |
|
| 148 |
+ expected := path.Join(tmp, "diff", "1") |
|
| 149 |
+ if diffPath != expected {
|
|
| 150 |
+ t.Fatalf("Expected path %s got %s", expected, diffPath)
|
|
| 151 |
+ } |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+func TestCleanupWithNoDirs(t *testing.T) {
|
|
| 155 |
+ d := newDriver(t) |
|
| 156 |
+ defer os.RemoveAll(tmp) |
|
| 157 |
+ |
|
| 158 |
+ if err := d.Cleanup(); err != nil {
|
|
| 159 |
+ t.Fatal(err) |
|
| 160 |
+ } |
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+func TestCleanupWithDir(t *testing.T) {
|
|
| 164 |
+ d := newDriver(t) |
|
| 165 |
+ defer os.RemoveAll(tmp) |
|
| 166 |
+ |
|
| 167 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 168 |
+ t.Fatal(err) |
|
| 169 |
+ } |
|
| 170 |
+ |
|
| 171 |
+ if err := d.Cleanup(); err != nil {
|
|
| 172 |
+ t.Fatal(err) |
|
| 173 |
+ } |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+func TestMountedFalseResponse(t *testing.T) {
|
|
| 177 |
+ d := newDriver(t) |
|
| 178 |
+ defer os.RemoveAll(tmp) |
|
| 179 |
+ |
|
| 180 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 181 |
+ t.Fatal(err) |
|
| 182 |
+ } |
|
| 183 |
+ |
|
| 184 |
+ response, err := d.mounted("1")
|
|
| 185 |
+ if err != nil {
|
|
| 186 |
+ t.Fatal(err) |
|
| 187 |
+ } |
|
| 188 |
+ |
|
| 189 |
+ if response != false {
|
|
| 190 |
+ t.Fatalf("Response if dir id 1 is mounted should be false")
|
|
| 191 |
+ } |
|
| 192 |
+} |
|
| 193 |
+ |
|
| 194 |
+func TestMountedTrueReponse(t *testing.T) {
|
|
| 195 |
+ d := newDriver(t) |
|
| 196 |
+ defer os.RemoveAll(tmp) |
|
| 197 |
+ defer d.Cleanup() |
|
| 198 |
+ |
|
| 199 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 200 |
+ t.Fatal(err) |
|
| 201 |
+ } |
|
| 202 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 203 |
+ t.Fatal(err) |
|
| 204 |
+ } |
|
| 205 |
+ |
|
| 206 |
+ _, err := d.Get("2")
|
|
| 207 |
+ if err != nil {
|
|
| 208 |
+ t.Fatal(err) |
|
| 209 |
+ } |
|
| 210 |
+ |
|
| 211 |
+ response, err := d.mounted("2")
|
|
| 212 |
+ if err != nil {
|
|
| 213 |
+ t.Fatal(err) |
|
| 214 |
+ } |
|
| 215 |
+ |
|
| 216 |
+ if response != true {
|
|
| 217 |
+ t.Fatalf("Response if dir id 2 is mounted should be true")
|
|
| 218 |
+ } |
|
| 219 |
+} |
|
| 220 |
+ |
|
| 221 |
+func TestMountWithParent(t *testing.T) {
|
|
| 222 |
+ d := newDriver(t) |
|
| 223 |
+ defer os.RemoveAll(tmp) |
|
| 224 |
+ |
|
| 225 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 226 |
+ t.Fatal(err) |
|
| 227 |
+ } |
|
| 228 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 229 |
+ t.Fatal(err) |
|
| 230 |
+ } |
|
| 231 |
+ |
|
| 232 |
+ defer func() {
|
|
| 233 |
+ if err := d.Cleanup(); err != nil {
|
|
| 234 |
+ t.Fatal(err) |
|
| 235 |
+ } |
|
| 236 |
+ }() |
|
| 237 |
+ |
|
| 238 |
+ mntPath, err := d.Get("2")
|
|
| 239 |
+ if err != nil {
|
|
| 240 |
+ t.Fatal(err) |
|
| 241 |
+ } |
|
| 242 |
+ if mntPath == "" {
|
|
| 243 |
+ t.Fatal("mntPath should not be empty string")
|
|
| 244 |
+ } |
|
| 245 |
+ |
|
| 246 |
+ expected := path.Join(tmp, "mnt", "2") |
|
| 247 |
+ if mntPath != expected {
|
|
| 248 |
+ t.Fatalf("Expected %s got %s", expected, mntPath)
|
|
| 249 |
+ } |
|
| 250 |
+} |
|
| 251 |
+ |
|
| 252 |
+func TestRemoveMountedDir(t *testing.T) {
|
|
| 253 |
+ d := newDriver(t) |
|
| 254 |
+ defer os.RemoveAll(tmp) |
|
| 255 |
+ |
|
| 256 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 257 |
+ t.Fatal(err) |
|
| 258 |
+ } |
|
| 259 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 260 |
+ t.Fatal(err) |
|
| 261 |
+ } |
|
| 262 |
+ |
|
| 263 |
+ defer func() {
|
|
| 264 |
+ if err := d.Cleanup(); err != nil {
|
|
| 265 |
+ t.Fatal(err) |
|
| 266 |
+ } |
|
| 267 |
+ }() |
|
| 268 |
+ |
|
| 269 |
+ mntPath, err := d.Get("2")
|
|
| 270 |
+ if err != nil {
|
|
| 271 |
+ t.Fatal(err) |
|
| 272 |
+ } |
|
| 273 |
+ if mntPath == "" {
|
|
| 274 |
+ t.Fatal("mntPath should not be empty string")
|
|
| 275 |
+ } |
|
| 276 |
+ |
|
| 277 |
+ mounted, err := d.mounted("2")
|
|
| 278 |
+ if err != nil {
|
|
| 279 |
+ t.Fatal(err) |
|
| 280 |
+ } |
|
| 281 |
+ |
|
| 282 |
+ if !mounted {
|
|
| 283 |
+ t.Fatalf("Dir id 2 should be mounted")
|
|
| 284 |
+ } |
|
| 285 |
+ |
|
| 286 |
+ if err := d.Remove("2"); err != nil {
|
|
| 287 |
+ t.Fatal(err) |
|
| 288 |
+ } |
|
| 289 |
+} |
|
| 290 |
+ |
|
| 291 |
+func TestCreateWithInvalidParent(t *testing.T) {
|
|
| 292 |
+ d := newDriver(t) |
|
| 293 |
+ defer os.RemoveAll(tmp) |
|
| 294 |
+ |
|
| 295 |
+ if err := d.Create("1", "docker"); err == nil {
|
|
| 296 |
+ t.Fatalf("Error should not be nil with parent does not exist")
|
|
| 297 |
+ } |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+func TestGetDiff(t *testing.T) {
|
|
| 301 |
+ d := newDriver(t) |
|
| 302 |
+ defer os.RemoveAll(tmp) |
|
| 303 |
+ |
|
| 304 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 305 |
+ t.Fatal(err) |
|
| 306 |
+ } |
|
| 307 |
+ |
|
| 308 |
+ diffPath, err := d.Get("1")
|
|
| 309 |
+ if err != nil {
|
|
| 310 |
+ t.Fatal(err) |
|
| 311 |
+ } |
|
| 312 |
+ |
|
| 313 |
+ // Add a file to the diff path with a fixed size |
|
| 314 |
+ size := int64(1024) |
|
| 315 |
+ |
|
| 316 |
+ f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 317 |
+ if err != nil {
|
|
| 318 |
+ t.Fatal(err) |
|
| 319 |
+ } |
|
| 320 |
+ if err := f.Truncate(size); err != nil {
|
|
| 321 |
+ t.Fatal(err) |
|
| 322 |
+ } |
|
| 323 |
+ f.Close() |
|
| 324 |
+ |
|
| 325 |
+ a, err := d.Diff("1")
|
|
| 326 |
+ if err != nil {
|
|
| 327 |
+ t.Fatal(err) |
|
| 328 |
+ } |
|
| 329 |
+ if a == nil {
|
|
| 330 |
+ t.Fatalf("Archive should not be nil")
|
|
| 331 |
+ } |
|
| 332 |
+} |
|
| 333 |
+ |
|
| 334 |
+func TestChanges(t *testing.T) {
|
|
| 335 |
+ d := newDriver(t) |
|
| 336 |
+ defer os.RemoveAll(tmp) |
|
| 337 |
+ |
|
| 338 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 339 |
+ t.Fatal(err) |
|
| 340 |
+ } |
|
| 341 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 342 |
+ t.Fatal(err) |
|
| 343 |
+ } |
|
| 344 |
+ |
|
| 345 |
+ defer func() {
|
|
| 346 |
+ if err := d.Cleanup(); err != nil {
|
|
| 347 |
+ t.Fatal(err) |
|
| 348 |
+ } |
|
| 349 |
+ }() |
|
| 350 |
+ |
|
| 351 |
+ mntPoint, err := d.Get("2")
|
|
| 352 |
+ if err != nil {
|
|
| 353 |
+ t.Fatal(err) |
|
| 354 |
+ } |
|
| 355 |
+ |
|
| 356 |
+ // Create a file to save in the mountpoint |
|
| 357 |
+ f, err := os.Create(path.Join(mntPoint, "test.txt")) |
|
| 358 |
+ if err != nil {
|
|
| 359 |
+ t.Fatal(err) |
|
| 360 |
+ } |
|
| 361 |
+ |
|
| 362 |
+ if _, err := f.WriteString("testline"); err != nil {
|
|
| 363 |
+ t.Fatal(err) |
|
| 364 |
+ } |
|
| 365 |
+ if err := f.Close(); err != nil {
|
|
| 366 |
+ t.Fatal(err) |
|
| 367 |
+ } |
|
| 368 |
+ |
|
| 369 |
+ changes, err := d.Changes("2")
|
|
| 370 |
+ if err != nil {
|
|
| 371 |
+ t.Fatal(err) |
|
| 372 |
+ } |
|
| 373 |
+ if len(changes) != 1 {
|
|
| 374 |
+ t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 375 |
+ } |
|
| 376 |
+ change := changes[0] |
|
| 377 |
+ |
|
| 378 |
+ expectedPath := "/test.txt" |
|
| 379 |
+ if change.Path != expectedPath {
|
|
| 380 |
+ t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 381 |
+ } |
|
| 382 |
+ |
|
| 383 |
+ if change.Kind != archive.ChangeAdd {
|
|
| 384 |
+ t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 385 |
+ } |
|
| 386 |
+ |
|
| 387 |
+ if err := d.Create("3", "2"); err != nil {
|
|
| 388 |
+ t.Fatal(err) |
|
| 389 |
+ } |
|
| 390 |
+ mntPoint, err = d.Get("3")
|
|
| 391 |
+ if err != nil {
|
|
| 392 |
+ t.Fatal(err) |
|
| 393 |
+ } |
|
| 394 |
+ |
|
| 395 |
+ // Create a file to save in the mountpoint |
|
| 396 |
+ f, err = os.Create(path.Join(mntPoint, "test2.txt")) |
|
| 397 |
+ if err != nil {
|
|
| 398 |
+ t.Fatal(err) |
|
| 399 |
+ } |
|
| 400 |
+ |
|
| 401 |
+ if _, err := f.WriteString("testline"); err != nil {
|
|
| 402 |
+ t.Fatal(err) |
|
| 403 |
+ } |
|
| 404 |
+ if err := f.Close(); err != nil {
|
|
| 405 |
+ t.Fatal(err) |
|
| 406 |
+ } |
|
| 407 |
+ |
|
| 408 |
+ changes, err = d.Changes("3")
|
|
| 409 |
+ if err != nil {
|
|
| 410 |
+ t.Fatal(err) |
|
| 411 |
+ } |
|
| 412 |
+ |
|
| 413 |
+ if len(changes) != 1 {
|
|
| 414 |
+ t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 415 |
+ } |
|
| 416 |
+ change = changes[0] |
|
| 417 |
+ |
|
| 418 |
+ expectedPath = "/test2.txt" |
|
| 419 |
+ if change.Path != expectedPath {
|
|
| 420 |
+ t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 421 |
+ } |
|
| 422 |
+ |
|
| 423 |
+ if change.Kind != archive.ChangeAdd {
|
|
| 424 |
+ t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 425 |
+ } |
|
| 426 |
+} |
|
| 427 |
+ |
|
| 428 |
+func TestDiffSize(t *testing.T) {
|
|
| 429 |
+ d := newDriver(t) |
|
| 430 |
+ defer os.RemoveAll(tmp) |
|
| 431 |
+ |
|
| 432 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 433 |
+ t.Fatal(err) |
|
| 434 |
+ } |
|
| 435 |
+ |
|
| 436 |
+ diffPath, err := d.Get("1")
|
|
| 437 |
+ if err != nil {
|
|
| 438 |
+ t.Fatal(err) |
|
| 439 |
+ } |
|
| 440 |
+ |
|
| 441 |
+ // Add a file to the diff path with a fixed size |
|
| 442 |
+ size := int64(1024) |
|
| 443 |
+ |
|
| 444 |
+ f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 445 |
+ if err != nil {
|
|
| 446 |
+ t.Fatal(err) |
|
| 447 |
+ } |
|
| 448 |
+ f.Truncate(size) |
|
| 449 |
+ s, err := f.Stat() |
|
| 450 |
+ if err != nil {
|
|
| 451 |
+ t.Fatal(err) |
|
| 452 |
+ } |
|
| 453 |
+ size = s.Size() |
|
| 454 |
+ if err := f.Close(); err != nil {
|
|
| 455 |
+ t.Fatal(err) |
|
| 456 |
+ } |
|
| 457 |
+ |
|
| 458 |
+ diffSize, err := d.Size("1")
|
|
| 459 |
+ if err != nil {
|
|
| 460 |
+ t.Fatal(err) |
|
| 461 |
+ } |
|
| 462 |
+ if diffSize != size {
|
|
| 463 |
+ t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
| 464 |
+ } |
|
| 465 |
+} |
|
| 466 |
+ |
|
| 467 |
+func TestApplyDiff(t *testing.T) {
|
|
| 468 |
+ d := newDriver(t) |
|
| 469 |
+ defer os.RemoveAll(tmp) |
|
| 470 |
+ defer d.Cleanup() |
|
| 471 |
+ |
|
| 472 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 473 |
+ t.Fatal(err) |
|
| 474 |
+ } |
|
| 475 |
+ |
|
| 476 |
+ diffPath, err := d.Get("1")
|
|
| 477 |
+ if err != nil {
|
|
| 478 |
+ t.Fatal(err) |
|
| 479 |
+ } |
|
| 480 |
+ |
|
| 481 |
+ // Add a file to the diff path with a fixed size |
|
| 482 |
+ size := int64(1024) |
|
| 483 |
+ |
|
| 484 |
+ f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 485 |
+ if err != nil {
|
|
| 486 |
+ t.Fatal(err) |
|
| 487 |
+ } |
|
| 488 |
+ f.Truncate(size) |
|
| 489 |
+ f.Close() |
|
| 490 |
+ |
|
| 491 |
+ diff, err := d.Diff("1")
|
|
| 492 |
+ if err != nil {
|
|
| 493 |
+ t.Fatal(err) |
|
| 494 |
+ } |
|
| 495 |
+ |
|
| 496 |
+ if err := d.Create("2", ""); err != nil {
|
|
| 497 |
+ t.Fatal(err) |
|
| 498 |
+ } |
|
| 499 |
+ if err := d.Create("3", "2"); err != nil {
|
|
| 500 |
+ t.Fatal(err) |
|
| 501 |
+ } |
|
| 502 |
+ |
|
| 503 |
+ if err := d.ApplyDiff("3", diff); err != nil {
|
|
| 504 |
+ t.Fatal(err) |
|
| 505 |
+ } |
|
| 506 |
+ |
|
| 507 |
+ // Ensure that the file is in the mount point for id 3 |
|
| 508 |
+ |
|
| 509 |
+ mountPoint, err := d.Get("3")
|
|
| 510 |
+ if err != nil {
|
|
| 511 |
+ t.Fatal(err) |
|
| 512 |
+ } |
|
| 513 |
+ if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
|
|
| 514 |
+ t.Fatal(err) |
|
| 515 |
+ } |
|
| 516 |
+} |
| 0 | 517 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,46 @@ |
| 0 |
+package aufs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "io/ioutil" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// Return all the directories |
|
| 10 |
+func loadIds(root string) ([]string, error) {
|
|
| 11 |
+ dirs, err := ioutil.ReadDir(root) |
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ return nil, err |
|
| 14 |
+ } |
|
| 15 |
+ out := []string{}
|
|
| 16 |
+ for _, d := range dirs {
|
|
| 17 |
+ if !d.IsDir() {
|
|
| 18 |
+ out = append(out, d.Name()) |
|
| 19 |
+ } |
|
| 20 |
+ } |
|
| 21 |
+ return out, nil |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+// Read the layers file for the current id and return all the |
|
| 25 |
+// layers represented by new lines in the file |
|
| 26 |
+// |
|
| 27 |
+// If there are no lines in the file then the id has no parent |
|
| 28 |
+// and an empty slice is returned. |
|
| 29 |
+func getParentIds(root, id string) ([]string, error) {
|
|
| 30 |
+ f, err := os.Open(path.Join(root, "layers", id)) |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ return nil, err |
|
| 33 |
+ } |
|
| 34 |
+ defer f.Close() |
|
| 35 |
+ |
|
| 36 |
+ out := []string{}
|
|
| 37 |
+ s := bufio.NewScanner(f) |
|
| 38 |
+ |
|
| 39 |
+ for s.Scan() {
|
|
| 40 |
+ if t := s.Text(); t != "" {
|
|
| 41 |
+ out = append(out, s.Text()) |
|
| 42 |
+ } |
|
| 43 |
+ } |
|
| 44 |
+ return out, s.Err() |
|
| 45 |
+} |
| 0 | 46 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,37 @@ |
| 0 |
+package aufs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/utils" |
|
| 4 |
+ "os" |
|
| 5 |
+ "os/exec" |
|
| 6 |
+ "path/filepath" |
|
| 7 |
+ "syscall" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func Unmount(target string) error {
|
|
| 11 |
+ if err := exec.Command("auplink", target, "flush").Run(); err != nil {
|
|
| 12 |
+ utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
|
|
| 13 |
+ } |
|
| 14 |
+ if err := syscall.Unmount(target, 0); err != nil {
|
|
| 15 |
+ return err |
|
| 16 |
+ } |
|
| 17 |
+ return nil |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func Mounted(mountpoint string) (bool, error) {
|
|
| 21 |
+ mntpoint, err := os.Stat(mountpoint) |
|
| 22 |
+ if err != nil {
|
|
| 23 |
+ if os.IsNotExist(err) {
|
|
| 24 |
+ return false, nil |
|
| 25 |
+ } |
|
| 26 |
+ return false, err |
|
| 27 |
+ } |
|
| 28 |
+ parent, err := os.Stat(filepath.Join(mountpoint, "..")) |
|
| 29 |
+ if err != nil {
|
|
| 30 |
+ return false, err |
|
| 31 |
+ } |
|
| 32 |
+ mntpointSt := mntpoint.Sys().(*syscall.Stat_t) |
|
| 33 |
+ parentSt := parent.Sys().(*syscall.Stat_t) |
|
| 34 |
+ |
|
| 35 |
+ return mntpointSt.Dev != parentSt.Dev, nil |
|
| 36 |
+} |
| 0 | 7 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,803 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/dotcloud/docker/utils" |
|
| 6 |
+ "io" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "os" |
|
| 9 |
+ "os/exec" |
|
| 10 |
+ "path" |
|
| 11 |
+ "path/filepath" |
|
| 12 |
+ "strconv" |
|
| 13 |
+ "sync" |
|
| 14 |
+ "syscall" |
|
| 15 |
+ "time" |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+var ( |
|
| 19 |
+ DefaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024 |
|
| 20 |
+ DefaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024 |
|
| 21 |
+ DefaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024 |
|
| 22 |
+) |
|
| 23 |
+ |
|
| 24 |
+type DevInfo struct {
|
|
| 25 |
+ Hash string `json:"-"` |
|
| 26 |
+ DeviceId int `json:"device_id"` |
|
| 27 |
+ Size uint64 `json:"size"` |
|
| 28 |
+ TransactionId uint64 `json:"transaction_id"` |
|
| 29 |
+ Initialized bool `json:"initialized"` |
|
| 30 |
+ devices *DeviceSet `json:"-"` |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+type MetaData struct {
|
|
| 34 |
+ Devices map[string]*DevInfo `json:devices` |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+type DeviceSet struct {
|
|
| 38 |
+ MetaData |
|
| 39 |
+ sync.Mutex |
|
| 40 |
+ root string |
|
| 41 |
+ devicePrefix string |
|
| 42 |
+ TransactionId uint64 |
|
| 43 |
+ NewTransactionId uint64 |
|
| 44 |
+ nextFreeDevice int |
|
| 45 |
+ activeMounts map[string]int |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+type DiskUsage struct {
|
|
| 49 |
+ Used uint64 |
|
| 50 |
+ Total uint64 |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+type Status struct {
|
|
| 54 |
+ PoolName string |
|
| 55 |
+ DataLoopback string |
|
| 56 |
+ MetadataLoopback string |
|
| 57 |
+ Data DiskUsage |
|
| 58 |
+ Metadata DiskUsage |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func getDevName(name string) string {
|
|
| 62 |
+ return "/dev/mapper/" + name |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+func (info *DevInfo) Name() string {
|
|
| 66 |
+ hash := info.Hash |
|
| 67 |
+ if hash == "" {
|
|
| 68 |
+ hash = "base" |
|
| 69 |
+ } |
|
| 70 |
+ return fmt.Sprintf("%s-%s", info.devices.devicePrefix, hash)
|
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func (info *DevInfo) DevName() string {
|
|
| 74 |
+ return getDevName(info.Name()) |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+func (devices *DeviceSet) loopbackDir() string {
|
|
| 78 |
+ return path.Join(devices.root, "devicemapper") |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func (devices *DeviceSet) jsonFile() string {
|
|
| 82 |
+ return path.Join(devices.loopbackDir(), "json") |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func (devices *DeviceSet) getPoolName() string {
|
|
| 86 |
+ return devices.devicePrefix + "-pool" |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+func (devices *DeviceSet) getPoolDevName() string {
|
|
| 90 |
+ return getDevName(devices.getPoolName()) |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+func (devices *DeviceSet) hasImage(name string) bool {
|
|
| 94 |
+ dirname := devices.loopbackDir() |
|
| 95 |
+ filename := path.Join(dirname, name) |
|
| 96 |
+ |
|
| 97 |
+ _, err := os.Stat(filename) |
|
| 98 |
+ return err == nil |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+// ensureImage creates a sparse file of <size> bytes at the path |
|
| 102 |
+// <root>/devicemapper/<name>. |
|
| 103 |
+// If the file already exists, it does nothing. |
|
| 104 |
+// Either way it returns the full path. |
|
| 105 |
+func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
|
| 106 |
+ dirname := devices.loopbackDir() |
|
| 107 |
+ filename := path.Join(dirname, name) |
|
| 108 |
+ |
|
| 109 |
+ if err := os.MkdirAll(dirname, 0700); err != nil && !os.IsExist(err) {
|
|
| 110 |
+ return "", err |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ if _, err := os.Stat(filename); err != nil {
|
|
| 114 |
+ if !os.IsNotExist(err) {
|
|
| 115 |
+ return "", err |
|
| 116 |
+ } |
|
| 117 |
+ utils.Debugf("Creating loopback file %s for device-manage use", filename)
|
|
| 118 |
+ file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) |
|
| 119 |
+ if err != nil {
|
|
| 120 |
+ return "", err |
|
| 121 |
+ } |
|
| 122 |
+ defer file.Close() |
|
| 123 |
+ |
|
| 124 |
+ if err = file.Truncate(size); err != nil {
|
|
| 125 |
+ return "", err |
|
| 126 |
+ } |
|
| 127 |
+ } |
|
| 128 |
+ return filename, nil |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func (devices *DeviceSet) allocateDeviceId() int {
|
|
| 132 |
+ // TODO: Add smarter reuse of deleted devices |
|
| 133 |
+ id := devices.nextFreeDevice |
|
| 134 |
+ devices.nextFreeDevice = devices.nextFreeDevice + 1 |
|
| 135 |
+ return id |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+func (devices *DeviceSet) allocateTransactionId() uint64 {
|
|
| 139 |
+ devices.NewTransactionId = devices.NewTransactionId + 1 |
|
| 140 |
+ return devices.NewTransactionId |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func (devices *DeviceSet) saveMetadata() error {
|
|
| 144 |
+ jsonData, err := json.Marshal(devices.MetaData) |
|
| 145 |
+ if err != nil {
|
|
| 146 |
+ return fmt.Errorf("Error encoding metaadata to json: %s", err)
|
|
| 147 |
+ } |
|
| 148 |
+ tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json") |
|
| 149 |
+ if err != nil {
|
|
| 150 |
+ return fmt.Errorf("Error creating metadata file: %s", err)
|
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ n, err := tmpFile.Write(jsonData) |
|
| 154 |
+ if err != nil {
|
|
| 155 |
+ return fmt.Errorf("Error writing metadata to %s: %s", tmpFile.Name(), err)
|
|
| 156 |
+ } |
|
| 157 |
+ if n < len(jsonData) {
|
|
| 158 |
+ return io.ErrShortWrite |
|
| 159 |
+ } |
|
| 160 |
+ if err := tmpFile.Sync(); err != nil {
|
|
| 161 |
+ return fmt.Errorf("Error syncing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 162 |
+ } |
|
| 163 |
+ if err := tmpFile.Close(); err != nil {
|
|
| 164 |
+ return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 165 |
+ } |
|
| 166 |
+ if err := os.Rename(tmpFile.Name(), devices.jsonFile()); err != nil {
|
|
| 167 |
+ return fmt.Errorf("Error committing metadata file", err)
|
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ if devices.NewTransactionId != devices.TransactionId {
|
|
| 171 |
+ if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
|
|
| 172 |
+ return fmt.Errorf("Error setting devmapper transition ID: %s", err)
|
|
| 173 |
+ } |
|
| 174 |
+ devices.TransactionId = devices.NewTransactionId |
|
| 175 |
+ } |
|
| 176 |
+ return nil |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) {
|
|
| 180 |
+ utils.Debugf("registerDevice(%v, %v)", id, hash)
|
|
| 181 |
+ info := &DevInfo{
|
|
| 182 |
+ Hash: hash, |
|
| 183 |
+ DeviceId: id, |
|
| 184 |
+ Size: size, |
|
| 185 |
+ TransactionId: devices.allocateTransactionId(), |
|
| 186 |
+ Initialized: false, |
|
| 187 |
+ devices: devices, |
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ devices.Devices[hash] = info |
|
| 191 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 192 |
+ // Try to remove unused device |
|
| 193 |
+ delete(devices.Devices, hash) |
|
| 194 |
+ return nil, err |
|
| 195 |
+ } |
|
| 196 |
+ |
|
| 197 |
+ return info, nil |
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error {
|
|
| 201 |
+ utils.Debugf("activateDeviceIfNeeded(%v)", hash)
|
|
| 202 |
+ info := devices.Devices[hash] |
|
| 203 |
+ if info == nil {
|
|
| 204 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 205 |
+ } |
|
| 206 |
+ |
|
| 207 |
+ if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
|
|
| 208 |
+ return nil |
|
| 209 |
+ } |
|
| 210 |
+ |
|
| 211 |
+ return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size) |
|
| 212 |
+} |
|
| 213 |
+ |
|
| 214 |
+func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
|
|
| 215 |
+ devname := info.DevName() |
|
| 216 |
+ |
|
| 217 |
+ err := exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname).Run()
|
|
| 218 |
+ if err != nil {
|
|
| 219 |
+ err = exec.Command("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname).Run()
|
|
| 220 |
+ } |
|
| 221 |
+ if err != nil {
|
|
| 222 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 223 |
+ return err |
|
| 224 |
+ } |
|
| 225 |
+ return nil |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+func (devices *DeviceSet) loadMetaData() error {
|
|
| 229 |
+ utils.Debugf("loadMetadata()")
|
|
| 230 |
+ defer utils.Debugf("loadMetadata END")
|
|
| 231 |
+ _, _, _, params, err := getStatus(devices.getPoolName()) |
|
| 232 |
+ if err != nil {
|
|
| 233 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 234 |
+ return err |
|
| 235 |
+ } |
|
| 236 |
+ |
|
| 237 |
+ if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
|
|
| 238 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 239 |
+ return err |
|
| 240 |
+ } |
|
| 241 |
+ devices.NewTransactionId = devices.TransactionId |
|
| 242 |
+ |
|
| 243 |
+ jsonData, err := ioutil.ReadFile(devices.jsonFile()) |
|
| 244 |
+ if err != nil && !os.IsNotExist(err) {
|
|
| 245 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 246 |
+ return err |
|
| 247 |
+ } |
|
| 248 |
+ |
|
| 249 |
+ devices.MetaData.Devices = make(map[string]*DevInfo) |
|
| 250 |
+ if jsonData != nil {
|
|
| 251 |
+ if err := json.Unmarshal(jsonData, &devices.MetaData); err != nil {
|
|
| 252 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 253 |
+ return err |
|
| 254 |
+ } |
|
| 255 |
+ } |
|
| 256 |
+ |
|
| 257 |
+ for hash, d := range devices.Devices {
|
|
| 258 |
+ d.Hash = hash |
|
| 259 |
+ d.devices = devices |
|
| 260 |
+ |
|
| 261 |
+ if d.DeviceId >= devices.nextFreeDevice {
|
|
| 262 |
+ devices.nextFreeDevice = d.DeviceId + 1 |
|
| 263 |
+ } |
|
| 264 |
+ |
|
| 265 |
+ // If the transaction id is larger than the actual one we lost the device due to some crash |
|
| 266 |
+ if d.TransactionId > devices.TransactionId {
|
|
| 267 |
+ utils.Debugf("Removing lost device %s with id %d", hash, d.TransactionId)
|
|
| 268 |
+ delete(devices.Devices, hash) |
|
| 269 |
+ } |
|
| 270 |
+ } |
|
| 271 |
+ return nil |
|
| 272 |
+} |
|
| 273 |
+ |
|
| 274 |
+func (devices *DeviceSet) setupBaseImage() error {
|
|
| 275 |
+ oldInfo := devices.Devices[""] |
|
| 276 |
+ if oldInfo != nil && oldInfo.Initialized {
|
|
| 277 |
+ return nil |
|
| 278 |
+ } |
|
| 279 |
+ |
|
| 280 |
+ if oldInfo != nil && !oldInfo.Initialized {
|
|
| 281 |
+ utils.Debugf("Removing uninitialized base image")
|
|
| 282 |
+ if err := devices.removeDevice(""); err != nil {
|
|
| 283 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 284 |
+ return err |
|
| 285 |
+ } |
|
| 286 |
+ } |
|
| 287 |
+ |
|
| 288 |
+ utils.Debugf("Initializing base device-manager snapshot")
|
|
| 289 |
+ |
|
| 290 |
+ id := devices.allocateDeviceId() |
|
| 291 |
+ |
|
| 292 |
+ // Create initial device |
|
| 293 |
+ if err := createDevice(devices.getPoolDevName(), id); err != nil {
|
|
| 294 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 295 |
+ return err |
|
| 296 |
+ } |
|
| 297 |
+ |
|
| 298 |
+ utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
|
|
| 299 |
+ info, err := devices.registerDevice(id, "", DefaultBaseFsSize) |
|
| 300 |
+ if err != nil {
|
|
| 301 |
+ _ = deleteDevice(devices.getPoolDevName(), id) |
|
| 302 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 303 |
+ return err |
|
| 304 |
+ } |
|
| 305 |
+ |
|
| 306 |
+ utils.Debugf("Creating filesystem on base device-manager snapshot")
|
|
| 307 |
+ |
|
| 308 |
+ if err = devices.activateDeviceIfNeeded(""); err != nil {
|
|
| 309 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 310 |
+ return err |
|
| 311 |
+ } |
|
| 312 |
+ |
|
| 313 |
+ if err := devices.createFilesystem(info); err != nil {
|
|
| 314 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 315 |
+ return err |
|
| 316 |
+ } |
|
| 317 |
+ |
|
| 318 |
+ info.Initialized = true |
|
| 319 |
+ if err = devices.saveMetadata(); err != nil {
|
|
| 320 |
+ info.Initialized = false |
|
| 321 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 322 |
+ return err |
|
| 323 |
+ } |
|
| 324 |
+ |
|
| 325 |
+ return nil |
|
| 326 |
+} |
|
| 327 |
+ |
|
| 328 |
+func setCloseOnExec(name string) {
|
|
| 329 |
+ fileInfos, _ := ioutil.ReadDir("/proc/self/fd")
|
|
| 330 |
+ if fileInfos != nil {
|
|
| 331 |
+ for _, i := range fileInfos {
|
|
| 332 |
+ link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name()))
|
|
| 333 |
+ if link == name {
|
|
| 334 |
+ fd, err := strconv.Atoi(i.Name()) |
|
| 335 |
+ if err == nil {
|
|
| 336 |
+ syscall.CloseOnExec(fd) |
|
| 337 |
+ } |
|
| 338 |
+ } |
|
| 339 |
+ } |
|
| 340 |
+ } |
|
| 341 |
+} |
|
| 342 |
+ |
|
| 343 |
+func (devices *DeviceSet) log(level int, file string, line int, dmError int, message string) {
|
|
| 344 |
+ if level >= 7 {
|
|
| 345 |
+ return // Ignore _LOG_DEBUG |
|
| 346 |
+ } |
|
| 347 |
+ |
|
| 348 |
+ utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 349 |
+} |
|
| 350 |
+ |
|
| 351 |
+func major(device uint64) uint64 {
|
|
| 352 |
+ return (device >> 8) & 0xfff |
|
| 353 |
+} |
|
| 354 |
+ |
|
| 355 |
+func minor(device uint64) uint64 {
|
|
| 356 |
+ return (device & 0xff) | ((device >> 12) & 0xfff00) |
|
| 357 |
+} |
|
| 358 |
+ |
|
| 359 |
+func (devices *DeviceSet) initDevmapper() error {
|
|
| 360 |
+ logInit(devices) |
|
| 361 |
+ |
|
| 362 |
+ // Make sure the sparse images exist in <root>/devicemapper/data and |
|
| 363 |
+ // <root>/devicemapper/metadata |
|
| 364 |
+ |
|
| 365 |
+ createdLoopback := !devices.hasImage("data") || !devices.hasImage("metadata")
|
|
| 366 |
+ data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
|
|
| 367 |
+ if err != nil {
|
|
| 368 |
+ utils.Debugf("Error device ensureImage (data): %s\n", err)
|
|
| 369 |
+ return err |
|
| 370 |
+ } |
|
| 371 |
+ metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
|
|
| 372 |
+ if err != nil {
|
|
| 373 |
+ utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
|
| 374 |
+ return err |
|
| 375 |
+ } |
|
| 376 |
+ |
|
| 377 |
+ // Set the device prefix from the device id and inode of the docker root dir |
|
| 378 |
+ |
|
| 379 |
+ st, err := os.Stat(devices.root) |
|
| 380 |
+ if err != nil {
|
|
| 381 |
+ return fmt.Errorf("Error looking up dir %s: %s", devices.root, err)
|
|
| 382 |
+ } |
|
| 383 |
+ sysSt := st.Sys().(*syscall.Stat_t) |
|
| 384 |
+ // "reg-" stands for "regular file". |
|
| 385 |
+ // In the future we might use "dev-" for "device file", etc. |
|
| 386 |
+ // docker-maj,min[-inode] stands for: |
|
| 387 |
+ // - Managed by docker |
|
| 388 |
+ // - The target of this device is at major <maj> and minor <min> |
|
| 389 |
+ // - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself. |
|
| 390 |
+ devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
|
|
| 391 |
+ utils.Debugf("Generated prefix: %s", devices.devicePrefix)
|
|
| 392 |
+ |
|
| 393 |
+ // Check for the existence of the device <prefix>-pool |
|
| 394 |
+ utils.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
|
|
| 395 |
+ info, err := getInfo(devices.getPoolName()) |
|
| 396 |
+ if info == nil {
|
|
| 397 |
+ utils.Debugf("Error device getInfo: %s", err)
|
|
| 398 |
+ return err |
|
| 399 |
+ } |
|
| 400 |
+ |
|
| 401 |
+ // It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files |
|
| 402 |
+ // that are not Close-on-exec, and lxc-start will die if it inherits any unexpected files, |
|
| 403 |
+ // so we add this badhack to make sure it closes itself |
|
| 404 |
+ setCloseOnExec("/dev/mapper/control")
|
|
| 405 |
+ |
|
| 406 |
+ // If the pool doesn't exist, create it |
|
| 407 |
+ if info.Exists == 0 {
|
|
| 408 |
+ utils.Debugf("Pool doesn't exist. Creating it.")
|
|
| 409 |
+ |
|
| 410 |
+ dataFile, err := AttachLoopDevice(data) |
|
| 411 |
+ if err != nil {
|
|
| 412 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 413 |
+ return err |
|
| 414 |
+ } |
|
| 415 |
+ defer dataFile.Close() |
|
| 416 |
+ |
|
| 417 |
+ metadataFile, err := AttachLoopDevice(metadata) |
|
| 418 |
+ if err != nil {
|
|
| 419 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 420 |
+ return err |
|
| 421 |
+ } |
|
| 422 |
+ defer metadataFile.Close() |
|
| 423 |
+ |
|
| 424 |
+ if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
|
|
| 425 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 426 |
+ return err |
|
| 427 |
+ } |
|
| 428 |
+ } |
|
| 429 |
+ |
|
| 430 |
+ // If we didn't just create the data or metadata image, we need to |
|
| 431 |
+ // load the metadata from the existing file. |
|
| 432 |
+ if !createdLoopback {
|
|
| 433 |
+ if err = devices.loadMetaData(); err != nil {
|
|
| 434 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 435 |
+ return err |
|
| 436 |
+ } |
|
| 437 |
+ } |
|
| 438 |
+ |
|
| 439 |
+ // Setup the base image |
|
| 440 |
+ if err := devices.setupBaseImage(); err != nil {
|
|
| 441 |
+ utils.Debugf("Error device setupBaseImage: %s\n", err)
|
|
| 442 |
+ return err |
|
| 443 |
+ } |
|
| 444 |
+ |
|
| 445 |
+ return nil |
|
| 446 |
+} |
|
| 447 |
+ |
|
| 448 |
+func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
|
|
| 449 |
+ devices.Lock() |
|
| 450 |
+ defer devices.Unlock() |
|
| 451 |
+ |
|
| 452 |
+ if devices.Devices[hash] != nil {
|
|
| 453 |
+ return fmt.Errorf("hash %s already exists", hash)
|
|
| 454 |
+ } |
|
| 455 |
+ |
|
| 456 |
+ baseInfo := devices.Devices[baseHash] |
|
| 457 |
+ if baseInfo == nil {
|
|
| 458 |
+ return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
|
|
| 459 |
+ } |
|
| 460 |
+ |
|
| 461 |
+ deviceId := devices.allocateDeviceId() |
|
| 462 |
+ |
|
| 463 |
+ if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
|
|
| 464 |
+ utils.Debugf("Error creating snap device: %s\n", err)
|
|
| 465 |
+ return err |
|
| 466 |
+ } |
|
| 467 |
+ |
|
| 468 |
+ if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
|
|
| 469 |
+ deleteDevice(devices.getPoolDevName(), deviceId) |
|
| 470 |
+ utils.Debugf("Error registering device: %s\n", err)
|
|
| 471 |
+ return err |
|
| 472 |
+ } |
|
| 473 |
+ return nil |
|
| 474 |
+} |
|
| 475 |
+ |
|
| 476 |
+func (devices *DeviceSet) removeDevice(hash string) error {
|
|
| 477 |
+ info := devices.Devices[hash] |
|
| 478 |
+ if info == nil {
|
|
| 479 |
+ return fmt.Errorf("hash %s doesn't exists", hash)
|
|
| 480 |
+ } |
|
| 481 |
+ |
|
| 482 |
+ devinfo, _ := getInfo(info.Name()) |
|
| 483 |
+ if devinfo != nil && devinfo.Exists != 0 {
|
|
| 484 |
+ if err := removeDevice(info.Name()); err != nil {
|
|
| 485 |
+ utils.Debugf("Error removing device: %s\n", err)
|
|
| 486 |
+ return err |
|
| 487 |
+ } |
|
| 488 |
+ } |
|
| 489 |
+ |
|
| 490 |
+ if info.Initialized {
|
|
| 491 |
+ info.Initialized = false |
|
| 492 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 493 |
+ utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 494 |
+ return err |
|
| 495 |
+ } |
|
| 496 |
+ } |
|
| 497 |
+ |
|
| 498 |
+ if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
|
|
| 499 |
+ utils.Debugf("Error deleting device: %s\n", err)
|
|
| 500 |
+ return err |
|
| 501 |
+ } |
|
| 502 |
+ |
|
| 503 |
+ devices.allocateTransactionId() |
|
| 504 |
+ delete(devices.Devices, info.Hash) |
|
| 505 |
+ |
|
| 506 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 507 |
+ devices.Devices[info.Hash] = info |
|
| 508 |
+ utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 509 |
+ return err |
|
| 510 |
+ } |
|
| 511 |
+ |
|
| 512 |
+ return nil |
|
| 513 |
+} |
|
| 514 |
+ |
|
| 515 |
+func (devices *DeviceSet) RemoveDevice(hash string) error {
|
|
| 516 |
+ devices.Lock() |
|
| 517 |
+ defer devices.Unlock() |
|
| 518 |
+ |
|
| 519 |
+ return devices.removeDevice(hash) |
|
| 520 |
+} |
|
| 521 |
+ |
|
| 522 |
+func (devices *DeviceSet) deactivateDevice(hash string) error {
|
|
| 523 |
+ utils.Debugf("[devmapper] deactivateDevice(%s)", hash)
|
|
| 524 |
+ defer utils.Debugf("[devmapper] deactivateDevice END")
|
|
| 525 |
+ var devname string |
|
| 526 |
+ // FIXME: shouldn't we just register the pool into devices? |
|
| 527 |
+ devname, err := devices.byHash(hash) |
|
| 528 |
+ if err != nil {
|
|
| 529 |
+ return err |
|
| 530 |
+ } |
|
| 531 |
+ devinfo, err := getInfo(devname) |
|
| 532 |
+ if err != nil {
|
|
| 533 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 534 |
+ return err |
|
| 535 |
+ } |
|
| 536 |
+ if devinfo.Exists != 0 {
|
|
| 537 |
+ if err := removeDevice(devname); err != nil {
|
|
| 538 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 539 |
+ return err |
|
| 540 |
+ } |
|
| 541 |
+ if err := devices.waitRemove(hash); err != nil {
|
|
| 542 |
+ return err |
|
| 543 |
+ } |
|
| 544 |
+ } |
|
| 545 |
+ |
|
| 546 |
+ return nil |
|
| 547 |
+} |
|
| 548 |
+ |
|
| 549 |
+// waitRemove blocks until either: |
|
| 550 |
+// a) the device registered at <device_set_prefix>-<hash> is removed, |
|
| 551 |
+// or b) the 1 second timeout expires. |
|
| 552 |
+func (devices *DeviceSet) waitRemove(hash string) error {
|
|
| 553 |
+ utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, hash)
|
|
| 554 |
+ defer utils.Debugf("[deviceset %s] waitRemove END", devices.devicePrefix, hash)
|
|
| 555 |
+ devname, err := devices.byHash(hash) |
|
| 556 |
+ if err != nil {
|
|
| 557 |
+ return err |
|
| 558 |
+ } |
|
| 559 |
+ i := 0 |
|
| 560 |
+ for ; i < 1000; i += 1 {
|
|
| 561 |
+ devinfo, err := getInfo(devname) |
|
| 562 |
+ if err != nil {
|
|
| 563 |
+ // If there is an error we assume the device doesn't exist. |
|
| 564 |
+ // The error might actually be something else, but we can't differentiate. |
|
| 565 |
+ return nil |
|
| 566 |
+ } |
|
| 567 |
+ utils.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
|
|
| 568 |
+ if devinfo.Exists == 0 {
|
|
| 569 |
+ break |
|
| 570 |
+ } |
|
| 571 |
+ time.Sleep(1 * time.Millisecond) |
|
| 572 |
+ } |
|
| 573 |
+ if i == 1000 {
|
|
| 574 |
+ return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
|
|
| 575 |
+ } |
|
| 576 |
+ return nil |
|
| 577 |
+} |
|
| 578 |
+ |
|
| 579 |
+// waitClose blocks until either: |
|
| 580 |
+// a) the device registered at <device_set_prefix>-<hash> is closed, |
|
| 581 |
+// or b) the 1 second timeout expires. |
|
| 582 |
+func (devices *DeviceSet) waitClose(hash string) error {
|
|
| 583 |
+ devname, err := devices.byHash(hash) |
|
| 584 |
+ if err != nil {
|
|
| 585 |
+ return err |
|
| 586 |
+ } |
|
| 587 |
+ i := 0 |
|
| 588 |
+ for ; i < 1000; i += 1 {
|
|
| 589 |
+ devinfo, err := getInfo(devname) |
|
| 590 |
+ if err != nil {
|
|
| 591 |
+ return err |
|
| 592 |
+ } |
|
| 593 |
+ utils.Debugf("Waiting for unmount of %s: opencount=%d", devname, devinfo.OpenCount)
|
|
| 594 |
+ if devinfo.OpenCount == 0 {
|
|
| 595 |
+ break |
|
| 596 |
+ } |
|
| 597 |
+ time.Sleep(1 * time.Millisecond) |
|
| 598 |
+ } |
|
| 599 |
+ if i == 1000 {
|
|
| 600 |
+ return fmt.Errorf("Timeout while waiting for device %s to close", devname)
|
|
| 601 |
+ } |
|
| 602 |
+ return nil |
|
| 603 |
+} |
|
| 604 |
+ |
|
| 605 |
+// byHash is a hack to allow looking up the deviceset's pool by the hash "pool". |
|
| 606 |
+// FIXME: it seems probably cleaner to register the pool in devices.Devices, |
|
| 607 |
+// but I am afraid of arcane implications deep in the devicemapper code, |
|
| 608 |
+// so this will do. |
|
| 609 |
+func (devices *DeviceSet) byHash(hash string) (devname string, err error) {
|
|
| 610 |
+ if hash == "pool" {
|
|
| 611 |
+ return devices.getPoolDevName(), nil |
|
| 612 |
+ } |
|
| 613 |
+ info := devices.Devices[hash] |
|
| 614 |
+ if info == nil {
|
|
| 615 |
+ return "", fmt.Errorf("hash %s doesn't exists", hash)
|
|
| 616 |
+ } |
|
| 617 |
+ return info.Name(), nil |
|
| 618 |
+} |
|
| 619 |
+ |
|
| 620 |
+func (devices *DeviceSet) Shutdown() error {
|
|
| 621 |
+ utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
|
|
| 622 |
+ defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
|
|
| 623 |
+ devices.Lock() |
|
| 624 |
+ utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
|
|
| 625 |
+ defer devices.Unlock() |
|
| 626 |
+ |
|
| 627 |
+ for path, count := range devices.activeMounts {
|
|
| 628 |
+ for i := count; i > 0; i-- {
|
|
| 629 |
+ if err := syscall.Unmount(path, 0); err != nil {
|
|
| 630 |
+ utils.Debugf("Shutdown unmounting %s, error: %s\n", path, err)
|
|
| 631 |
+ } |
|
| 632 |
+ } |
|
| 633 |
+ delete(devices.activeMounts, path) |
|
| 634 |
+ } |
|
| 635 |
+ |
|
| 636 |
+ for _, d := range devices.Devices {
|
|
| 637 |
+ if err := devices.waitClose(d.Hash); err != nil {
|
|
| 638 |
+ utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
|
|
| 639 |
+ } |
|
| 640 |
+ if err := devices.deactivateDevice(d.Hash); err != nil {
|
|
| 641 |
+ utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
|
|
| 642 |
+ } |
|
| 643 |
+ } |
|
| 644 |
+ |
|
| 645 |
+ pool := devices.getPoolDevName() |
|
| 646 |
+ if devinfo, err := getInfo(pool); err == nil && devinfo.Exists != 0 {
|
|
| 647 |
+ if err := devices.deactivateDevice("pool"); err != nil {
|
|
| 648 |
+ utils.Debugf("Shutdown deactivate %s , error: %s\n", pool, err)
|
|
| 649 |
+ } |
|
| 650 |
+ } |
|
| 651 |
+ |
|
| 652 |
+ return nil |
|
| 653 |
+} |
|
| 654 |
+ |
|
| 655 |
+func (devices *DeviceSet) MountDevice(hash, path string, readOnly bool) error {
|
|
| 656 |
+ devices.Lock() |
|
| 657 |
+ defer devices.Unlock() |
|
| 658 |
+ |
|
| 659 |
+ if err := devices.activateDeviceIfNeeded(hash); err != nil {
|
|
| 660 |
+ return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
|
|
| 661 |
+ } |
|
| 662 |
+ |
|
| 663 |
+ info := devices.Devices[hash] |
|
| 664 |
+ |
|
| 665 |
+ var flags uintptr = syscall.MS_MGC_VAL |
|
| 666 |
+ |
|
| 667 |
+ if readOnly {
|
|
| 668 |
+ flags = flags | syscall.MS_RDONLY |
|
| 669 |
+ } |
|
| 670 |
+ |
|
| 671 |
+ err := syscall.Mount(info.DevName(), path, "ext4", flags, "discard") |
|
| 672 |
+ if err != nil && err == syscall.EINVAL {
|
|
| 673 |
+ err = syscall.Mount(info.DevName(), path, "ext4", flags, "") |
|
| 674 |
+ } |
|
| 675 |
+ if err != nil {
|
|
| 676 |
+ return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
|
| 677 |
+ } |
|
| 678 |
+ |
|
| 679 |
+ count := devices.activeMounts[path] |
|
| 680 |
+ devices.activeMounts[path] = count + 1 |
|
| 681 |
+ |
|
| 682 |
+ return devices.setInitialized(hash) |
|
| 683 |
+} |
|
| 684 |
+ |
|
| 685 |
+func (devices *DeviceSet) UnmountDevice(hash, path string, deactivate bool) error {
|
|
| 686 |
+ utils.Debugf("[devmapper] UnmountDevice(hash=%s path=%s)", hash, path)
|
|
| 687 |
+ defer utils.Debugf("[devmapper] UnmountDevice END")
|
|
| 688 |
+ devices.Lock() |
|
| 689 |
+ defer devices.Unlock() |
|
| 690 |
+ |
|
| 691 |
+ utils.Debugf("[devmapper] Unmount(%s)", path)
|
|
| 692 |
+ if err := syscall.Unmount(path, 0); err != nil {
|
|
| 693 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 694 |
+ return err |
|
| 695 |
+ } |
|
| 696 |
+ utils.Debugf("[devmapper] Unmount done")
|
|
| 697 |
+ // Wait for the unmount to be effective, |
|
| 698 |
+ // by watching the value of Info.OpenCount for the device |
|
| 699 |
+ if err := devices.waitClose(hash); err != nil {
|
|
| 700 |
+ return err |
|
| 701 |
+ } |
|
| 702 |
+ |
|
| 703 |
+ if count := devices.activeMounts[path]; count > 1 {
|
|
| 704 |
+ devices.activeMounts[path] = count - 1 |
|
| 705 |
+ } else {
|
|
| 706 |
+ delete(devices.activeMounts, path) |
|
| 707 |
+ } |
|
| 708 |
+ |
|
| 709 |
+ if deactivate {
|
|
| 710 |
+ devices.deactivateDevice(hash) |
|
| 711 |
+ } |
|
| 712 |
+ |
|
| 713 |
+ return nil |
|
| 714 |
+} |
|
| 715 |
+ |
|
| 716 |
+func (devices *DeviceSet) HasDevice(hash string) bool {
|
|
| 717 |
+ devices.Lock() |
|
| 718 |
+ defer devices.Unlock() |
|
| 719 |
+ |
|
| 720 |
+ return devices.Devices[hash] != nil |
|
| 721 |
+} |
|
| 722 |
+ |
|
| 723 |
+func (devices *DeviceSet) HasInitializedDevice(hash string) bool {
|
|
| 724 |
+ devices.Lock() |
|
| 725 |
+ defer devices.Unlock() |
|
| 726 |
+ |
|
| 727 |
+ info := devices.Devices[hash] |
|
| 728 |
+ return info != nil && info.Initialized |
|
| 729 |
+} |
|
| 730 |
+ |
|
| 731 |
+func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
|
|
| 732 |
+ devices.Lock() |
|
| 733 |
+ defer devices.Unlock() |
|
| 734 |
+ |
|
| 735 |
+ info := devices.Devices[hash] |
|
| 736 |
+ if info == nil {
|
|
| 737 |
+ return false |
|
| 738 |
+ } |
|
| 739 |
+ devinfo, _ := getInfo(info.Name()) |
|
| 740 |
+ return devinfo != nil && devinfo.Exists != 0 |
|
| 741 |
+} |
|
| 742 |
+ |
|
| 743 |
+func (devices *DeviceSet) setInitialized(hash string) error {
|
|
| 744 |
+ info := devices.Devices[hash] |
|
| 745 |
+ if info == nil {
|
|
| 746 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 747 |
+ } |
|
| 748 |
+ |
|
| 749 |
+ info.Initialized = true |
|
| 750 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 751 |
+ info.Initialized = false |
|
| 752 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 753 |
+ return err |
|
| 754 |
+ } |
|
| 755 |
+ |
|
| 756 |
+ return nil |
|
| 757 |
+} |
|
| 758 |
+ |
|
| 759 |
+func (devices *DeviceSet) Status() *Status {
|
|
| 760 |
+ devices.Lock() |
|
| 761 |
+ defer devices.Unlock() |
|
| 762 |
+ |
|
| 763 |
+ status := &Status{}
|
|
| 764 |
+ |
|
| 765 |
+ status.PoolName = devices.getPoolName() |
|
| 766 |
+ status.DataLoopback = path.Join(devices.loopbackDir(), "data") |
|
| 767 |
+ status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") |
|
| 768 |
+ |
|
| 769 |
+ _, totalSizeInSectors, _, params, err := getStatus(devices.getPoolName()) |
|
| 770 |
+ if err == nil {
|
|
| 771 |
+ var transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64 |
|
| 772 |
+ if _, err := fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal); err == nil {
|
|
| 773 |
+ // Convert from blocks to bytes |
|
| 774 |
+ blockSizeInSectors := totalSizeInSectors / dataTotal |
|
| 775 |
+ |
|
| 776 |
+ status.Data.Used = dataUsed * blockSizeInSectors * 512 |
|
| 777 |
+ status.Data.Total = dataTotal * blockSizeInSectors * 512 |
|
| 778 |
+ |
|
| 779 |
+ // metadata blocks are always 4k |
|
| 780 |
+ status.Metadata.Used = metadataUsed * 4096 |
|
| 781 |
+ status.Metadata.Total = metadataTotal * 4096 |
|
| 782 |
+ } |
|
| 783 |
+ } |
|
| 784 |
+ |
|
| 785 |
+ return status |
|
| 786 |
+} |
|
| 787 |
+ |
|
| 788 |
+func NewDeviceSet(root string) (*DeviceSet, error) {
|
|
| 789 |
+ SetDevDir("/dev")
|
|
| 790 |
+ |
|
| 791 |
+ devices := &DeviceSet{
|
|
| 792 |
+ root: root, |
|
| 793 |
+ MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
|
| 794 |
+ activeMounts: make(map[string]int), |
|
| 795 |
+ } |
|
| 796 |
+ |
|
| 797 |
+ if err := devices.initDevmapper(); err != nil {
|
|
| 798 |
+ return nil, err |
|
| 799 |
+ } |
|
| 800 |
+ |
|
| 801 |
+ return devices, nil |
|
| 802 |
+} |
| 0 | 803 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,505 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/dotcloud/docker/utils" |
|
| 6 |
+ "os" |
|
| 7 |
+ "runtime" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type DevmapperLogger interface {
|
|
| 11 |
+ log(level int, file string, line int, dmError int, message string) |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+const ( |
|
| 15 |
+ DeviceCreate TaskType = iota |
|
| 16 |
+ DeviceReload |
|
| 17 |
+ DeviceRemove |
|
| 18 |
+ DeviceRemoveAll |
|
| 19 |
+ DeviceSuspend |
|
| 20 |
+ DeviceResume |
|
| 21 |
+ DeviceInfo |
|
| 22 |
+ DeviceDeps |
|
| 23 |
+ DeviceRename |
|
| 24 |
+ DeviceVersion |
|
| 25 |
+ DeviceStatus |
|
| 26 |
+ DeviceTable |
|
| 27 |
+ DeviceWaitevent |
|
| 28 |
+ DeviceList |
|
| 29 |
+ DeviceClear |
|
| 30 |
+ DeviceMknodes |
|
| 31 |
+ DeviceListVersions |
|
| 32 |
+ DeviceTargetMsg |
|
| 33 |
+ DeviceSetGeometry |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+const ( |
|
| 37 |
+ AddNodeOnResume AddNodeType = iota |
|
| 38 |
+ AddNodeOnCreate |
|
| 39 |
+) |
|
| 40 |
+ |
|
| 41 |
+var ( |
|
| 42 |
+ ErrTaskRun = errors.New("dm_task_run failed")
|
|
| 43 |
+ ErrTaskSetName = errors.New("dm_task_set_name failed")
|
|
| 44 |
+ ErrTaskSetMessage = errors.New("dm_task_set_message failed")
|
|
| 45 |
+ ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed")
|
|
| 46 |
+ ErrTaskSetRo = errors.New("dm_task_set_ro failed")
|
|
| 47 |
+ ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
|
| 48 |
+ ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
|
| 49 |
+ ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
|
| 50 |
+ ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
|
|
| 51 |
+ ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
|
| 52 |
+ ErrNilCookie = errors.New("cookie ptr can't be nil")
|
|
| 53 |
+ ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
|
|
| 54 |
+ ErrGetBlockSize = errors.New("Can't get block size")
|
|
| 55 |
+ ErrUdevWait = errors.New("wait on udev cookie failed")
|
|
| 56 |
+ ErrSetDevDir = errors.New("dm_set_dev_dir failed")
|
|
| 57 |
+ ErrGetLibraryVersion = errors.New("dm_get_library_version failed")
|
|
| 58 |
+ ErrCreateRemoveTask = errors.New("Can't create task of type DeviceRemove")
|
|
| 59 |
+ ErrRunRemoveDevice = errors.New("running removeDevice failed")
|
|
| 60 |
+ ErrInvalidAddNode = errors.New("Invalide AddNoce type")
|
|
| 61 |
+) |
|
| 62 |
+ |
|
| 63 |
+type ( |
|
| 64 |
+ Task struct {
|
|
| 65 |
+ unmanaged *CDmTask |
|
| 66 |
+ } |
|
| 67 |
+ Info struct {
|
|
| 68 |
+ Exists int |
|
| 69 |
+ Suspended int |
|
| 70 |
+ LiveTable int |
|
| 71 |
+ InactiveTable int |
|
| 72 |
+ OpenCount int32 |
|
| 73 |
+ EventNr uint32 |
|
| 74 |
+ Major uint32 |
|
| 75 |
+ Minor uint32 |
|
| 76 |
+ ReadOnly int |
|
| 77 |
+ TargetCount int32 |
|
| 78 |
+ } |
|
| 79 |
+ TaskType int |
|
| 80 |
+ AddNodeType int |
|
| 81 |
+) |
|
| 82 |
+ |
|
| 83 |
+func (t *Task) destroy() {
|
|
| 84 |
+ if t != nil {
|
|
| 85 |
+ DmTaskDestory(t.unmanaged) |
|
| 86 |
+ runtime.SetFinalizer(t, nil) |
|
| 87 |
+ } |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func TaskCreate(tasktype TaskType) *Task {
|
|
| 91 |
+ Ctask := DmTaskCreate(int(tasktype)) |
|
| 92 |
+ if Ctask == nil {
|
|
| 93 |
+ return nil |
|
| 94 |
+ } |
|
| 95 |
+ task := &Task{unmanaged: Ctask}
|
|
| 96 |
+ runtime.SetFinalizer(task, (*Task).destroy) |
|
| 97 |
+ return task |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (t *Task) Run() error {
|
|
| 101 |
+ if res := DmTaskRun(t.unmanaged); res != 1 {
|
|
| 102 |
+ return ErrTaskRun |
|
| 103 |
+ } |
|
| 104 |
+ return nil |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func (t *Task) SetName(name string) error {
|
|
| 108 |
+ if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
|
| 109 |
+ return ErrTaskSetName |
|
| 110 |
+ } |
|
| 111 |
+ return nil |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func (t *Task) SetMessage(message string) error {
|
|
| 115 |
+ if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
|
| 116 |
+ return ErrTaskSetMessage |
|
| 117 |
+ } |
|
| 118 |
+ return nil |
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+func (t *Task) SetSector(sector uint64) error {
|
|
| 122 |
+ if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
|
| 123 |
+ return ErrTaskSetSector |
|
| 124 |
+ } |
|
| 125 |
+ return nil |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+func (t *Task) SetCookie(cookie *uint, flags uint16) error {
|
|
| 129 |
+ if cookie == nil {
|
|
| 130 |
+ return ErrNilCookie |
|
| 131 |
+ } |
|
| 132 |
+ if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
|
|
| 133 |
+ return ErrTaskSetCookie |
|
| 134 |
+ } |
|
| 135 |
+ return nil |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+func (t *Task) SetAddNode(addNode AddNodeType) error {
|
|
| 139 |
+ if addNode != AddNodeOnResume && addNode != AddNodeOnCreate {
|
|
| 140 |
+ return ErrInvalidAddNode |
|
| 141 |
+ } |
|
| 142 |
+ if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
|
| 143 |
+ return ErrTaskSetAddNode |
|
| 144 |
+ } |
|
| 145 |
+ return nil |
|
| 146 |
+} |
|
| 147 |
+ |
|
| 148 |
+func (t *Task) SetRo() error {
|
|
| 149 |
+ if res := DmTaskSetRo(t.unmanaged); res != 1 {
|
|
| 150 |
+ return ErrTaskSetRo |
|
| 151 |
+ } |
|
| 152 |
+ return nil |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
|
|
| 156 |
+ if res := DmTaskAddTarget(t.unmanaged, start, size, |
|
| 157 |
+ ttype, params); res != 1 {
|
|
| 158 |
+ return ErrTaskAddTarget |
|
| 159 |
+ } |
|
| 160 |
+ return nil |
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+func (t *Task) GetInfo() (*Info, error) {
|
|
| 164 |
+ info := &Info{}
|
|
| 165 |
+ if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
|
| 166 |
+ return nil, ErrTaskGetInfo |
|
| 167 |
+ } |
|
| 168 |
+ return info, nil |
|
| 169 |
+} |
|
| 170 |
+ |
|
| 171 |
+func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, |
|
| 172 |
+ length uint64, targetType string, params string) {
|
|
| 173 |
+ |
|
| 174 |
+ return DmGetNextTarget(t.unmanaged, next, &start, &length, |
|
| 175 |
+ &targetType, ¶ms), |
|
| 176 |
+ start, length, targetType, params |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+func AttachLoopDevice(filename string) (*os.File, error) {
|
|
| 180 |
+ var fd int |
|
| 181 |
+ res := DmAttachLoopDevice(filename, &fd) |
|
| 182 |
+ if res == "" {
|
|
| 183 |
+ return nil, ErrAttachLoopbackDevice |
|
| 184 |
+ } |
|
| 185 |
+ return os.NewFile(uintptr(fd), res), nil |
|
| 186 |
+} |
|
| 187 |
+ |
|
| 188 |
+func UdevWait(cookie uint) error {
|
|
| 189 |
+ if res := DmUdevWait(cookie); res != 1 {
|
|
| 190 |
+ utils.Debugf("Failed to wait on udev cookie %d", cookie)
|
|
| 191 |
+ return ErrUdevWait |
|
| 192 |
+ } |
|
| 193 |
+ return nil |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+func LogInitVerbose(level int) {
|
|
| 197 |
+ DmLogInitVerbose(level) |
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+var dmLogger DevmapperLogger = nil |
|
| 201 |
+ |
|
| 202 |
+func logInit(logger DevmapperLogger) {
|
|
| 203 |
+ dmLogger = logger |
|
| 204 |
+ LogWithErrnoInit() |
|
| 205 |
+} |
|
| 206 |
+ |
|
| 207 |
+func SetDevDir(dir string) error {
|
|
| 208 |
+ if res := DmSetDevDir(dir); res != 1 {
|
|
| 209 |
+ utils.Debugf("Error dm_set_dev_dir")
|
|
| 210 |
+ return ErrSetDevDir |
|
| 211 |
+ } |
|
| 212 |
+ return nil |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 215 |
+func GetLibraryVersion() (string, error) {
|
|
| 216 |
+ var version string |
|
| 217 |
+ if res := DmGetLibraryVersion(&version); res != 1 {
|
|
| 218 |
+ return "", ErrGetLibraryVersion |
|
| 219 |
+ } |
|
| 220 |
+ return version, nil |
|
| 221 |
+} |
|
| 222 |
+ |
|
| 223 |
+// Useful helper for cleanup |
|
| 224 |
+func RemoveDevice(name string) error {
|
|
| 225 |
+ task := TaskCreate(DeviceRemove) |
|
| 226 |
+ if task == nil {
|
|
| 227 |
+ return ErrCreateRemoveTask |
|
| 228 |
+ } |
|
| 229 |
+ if err := task.SetName(name); err != nil {
|
|
| 230 |
+ utils.Debugf("Can't set task name %s", name)
|
|
| 231 |
+ return err |
|
| 232 |
+ } |
|
| 233 |
+ if err := task.Run(); err != nil {
|
|
| 234 |
+ return ErrRunRemoveDevice |
|
| 235 |
+ } |
|
| 236 |
+ return nil |
|
| 237 |
+} |
|
| 238 |
+ |
|
| 239 |
+func GetBlockDeviceSize(file *os.File) (uint64, error) {
|
|
| 240 |
+ size, errno := DmGetBlockSize(file.Fd()) |
|
| 241 |
+ if size == -1 || errno != 0 {
|
|
| 242 |
+ return 0, ErrGetBlockSize |
|
| 243 |
+ } |
|
| 244 |
+ return uint64(size), nil |
|
| 245 |
+} |
|
| 246 |
+ |
|
| 247 |
+// This is the programmatic example of "dmsetup create" |
|
| 248 |
+func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error {
|
|
| 249 |
+ task, err := createTask(DeviceCreate, poolName) |
|
| 250 |
+ if task == nil {
|
|
| 251 |
+ return err |
|
| 252 |
+ } |
|
| 253 |
+ |
|
| 254 |
+ size, err := GetBlockDeviceSize(dataFile) |
|
| 255 |
+ if err != nil {
|
|
| 256 |
+ return fmt.Errorf("Can't get data size")
|
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768" |
|
| 260 |
+ if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 261 |
+ return fmt.Errorf("Can't add target")
|
|
| 262 |
+ } |
|
| 263 |
+ |
|
| 264 |
+ var cookie uint = 0 |
|
| 265 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 266 |
+ return fmt.Errorf("Can't set cookie")
|
|
| 267 |
+ } |
|
| 268 |
+ |
|
| 269 |
+ if err := task.Run(); err != nil {
|
|
| 270 |
+ return fmt.Errorf("Error running DeviceCreate (createPool)")
|
|
| 271 |
+ } |
|
| 272 |
+ |
|
| 273 |
+ UdevWait(cookie) |
|
| 274 |
+ |
|
| 275 |
+ return nil |
|
| 276 |
+} |
|
| 277 |
+ |
|
| 278 |
+func createTask(t TaskType, name string) (*Task, error) {
|
|
| 279 |
+ task := TaskCreate(t) |
|
| 280 |
+ if task == nil {
|
|
| 281 |
+ return nil, fmt.Errorf("Can't create task of type %d", int(t))
|
|
| 282 |
+ } |
|
| 283 |
+ if err := task.SetName(name); err != nil {
|
|
| 284 |
+ return nil, fmt.Errorf("Can't set task name %s", name)
|
|
| 285 |
+ } |
|
| 286 |
+ return task, nil |
|
| 287 |
+} |
|
| 288 |
+ |
|
| 289 |
+func getInfo(name string) (*Info, error) {
|
|
| 290 |
+ task, err := createTask(DeviceInfo, name) |
|
| 291 |
+ if task == nil {
|
|
| 292 |
+ return nil, err |
|
| 293 |
+ } |
|
| 294 |
+ if err := task.Run(); err != nil {
|
|
| 295 |
+ return nil, err |
|
| 296 |
+ } |
|
| 297 |
+ return task.GetInfo() |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+func getStatus(name string) (uint64, uint64, string, string, error) {
|
|
| 301 |
+ task, err := createTask(DeviceStatus, name) |
|
| 302 |
+ if task == nil {
|
|
| 303 |
+ utils.Debugf("getStatus: Error createTask: %s", err)
|
|
| 304 |
+ return 0, 0, "", "", err |
|
| 305 |
+ } |
|
| 306 |
+ if err := task.Run(); err != nil {
|
|
| 307 |
+ utils.Debugf("getStatus: Error Run: %s", err)
|
|
| 308 |
+ return 0, 0, "", "", err |
|
| 309 |
+ } |
|
| 310 |
+ |
|
| 311 |
+ devinfo, err := task.GetInfo() |
|
| 312 |
+ if err != nil {
|
|
| 313 |
+ utils.Debugf("getStatus: Error GetInfo: %s", err)
|
|
| 314 |
+ return 0, 0, "", "", err |
|
| 315 |
+ } |
|
| 316 |
+ if devinfo.Exists == 0 {
|
|
| 317 |
+ utils.Debugf("getStatus: Non existing device %s", name)
|
|
| 318 |
+ return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
|
|
| 319 |
+ } |
|
| 320 |
+ |
|
| 321 |
+ _, start, length, target_type, params := task.GetNextTarget(0) |
|
| 322 |
+ return start, length, target_type, params, nil |
|
| 323 |
+} |
|
| 324 |
+ |
|
| 325 |
+func setTransactionId(poolName string, oldId uint64, newId uint64) error {
|
|
| 326 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 327 |
+ if task == nil {
|
|
| 328 |
+ return err |
|
| 329 |
+ } |
|
| 330 |
+ |
|
| 331 |
+ if err := task.SetSector(0); err != nil {
|
|
| 332 |
+ return fmt.Errorf("Can't set sector")
|
|
| 333 |
+ } |
|
| 334 |
+ |
|
| 335 |
+ if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
|
|
| 336 |
+ return fmt.Errorf("Can't set message")
|
|
| 337 |
+ } |
|
| 338 |
+ |
|
| 339 |
+ if err := task.Run(); err != nil {
|
|
| 340 |
+ return fmt.Errorf("Error running setTransactionId")
|
|
| 341 |
+ } |
|
| 342 |
+ return nil |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+func suspendDevice(name string) error {
|
|
| 346 |
+ task, err := createTask(DeviceSuspend, name) |
|
| 347 |
+ if task == nil {
|
|
| 348 |
+ return err |
|
| 349 |
+ } |
|
| 350 |
+ if err := task.Run(); err != nil {
|
|
| 351 |
+ return fmt.Errorf("Error running DeviceSuspend")
|
|
| 352 |
+ } |
|
| 353 |
+ return nil |
|
| 354 |
+} |
|
| 355 |
+ |
|
| 356 |
+func resumeDevice(name string) error {
|
|
| 357 |
+ task, err := createTask(DeviceResume, name) |
|
| 358 |
+ if task == nil {
|
|
| 359 |
+ return err |
|
| 360 |
+ } |
|
| 361 |
+ |
|
| 362 |
+ var cookie uint = 0 |
|
| 363 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 364 |
+ return fmt.Errorf("Can't set cookie")
|
|
| 365 |
+ } |
|
| 366 |
+ |
|
| 367 |
+ if err := task.Run(); err != nil {
|
|
| 368 |
+ return fmt.Errorf("Error running DeviceSuspend")
|
|
| 369 |
+ } |
|
| 370 |
+ |
|
| 371 |
+ UdevWait(cookie) |
|
| 372 |
+ |
|
| 373 |
+ return nil |
|
| 374 |
+} |
|
| 375 |
+ |
|
| 376 |
+func createDevice(poolName string, deviceId int) error {
|
|
| 377 |
+ utils.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
|
|
| 378 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 379 |
+ if task == nil {
|
|
| 380 |
+ return err |
|
| 381 |
+ } |
|
| 382 |
+ |
|
| 383 |
+ if err := task.SetSector(0); err != nil {
|
|
| 384 |
+ return fmt.Errorf("Can't set sector")
|
|
| 385 |
+ } |
|
| 386 |
+ |
|
| 387 |
+ if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
|
|
| 388 |
+ return fmt.Errorf("Can't set message")
|
|
| 389 |
+ } |
|
| 390 |
+ |
|
| 391 |
+ if err := task.Run(); err != nil {
|
|
| 392 |
+ return fmt.Errorf("Error running createDevice")
|
|
| 393 |
+ } |
|
| 394 |
+ return nil |
|
| 395 |
+} |
|
| 396 |
+ |
|
| 397 |
+func deleteDevice(poolName string, deviceId int) error {
|
|
| 398 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 399 |
+ if task == nil {
|
|
| 400 |
+ return err |
|
| 401 |
+ } |
|
| 402 |
+ |
|
| 403 |
+ if err := task.SetSector(0); err != nil {
|
|
| 404 |
+ return fmt.Errorf("Can't set sector")
|
|
| 405 |
+ } |
|
| 406 |
+ |
|
| 407 |
+ if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
|
|
| 408 |
+ return fmt.Errorf("Can't set message")
|
|
| 409 |
+ } |
|
| 410 |
+ |
|
| 411 |
+ if err := task.Run(); err != nil {
|
|
| 412 |
+ return fmt.Errorf("Error running deleteDevice")
|
|
| 413 |
+ } |
|
| 414 |
+ return nil |
|
| 415 |
+} |
|
| 416 |
+ |
|
| 417 |
+func removeDevice(name string) error {
|
|
| 418 |
+ utils.Debugf("[devmapper] removeDevice START")
|
|
| 419 |
+ defer utils.Debugf("[devmapper] removeDevice END")
|
|
| 420 |
+ task, err := createTask(DeviceRemove, name) |
|
| 421 |
+ if task == nil {
|
|
| 422 |
+ return err |
|
| 423 |
+ } |
|
| 424 |
+ if err = task.Run(); err != nil {
|
|
| 425 |
+ return fmt.Errorf("Error running removeDevice")
|
|
| 426 |
+ } |
|
| 427 |
+ return nil |
|
| 428 |
+} |
|
| 429 |
+ |
|
| 430 |
+func activateDevice(poolName string, name string, deviceId int, size uint64) error {
|
|
| 431 |
+ task, err := createTask(DeviceCreate, name) |
|
| 432 |
+ if task == nil {
|
|
| 433 |
+ return err |
|
| 434 |
+ } |
|
| 435 |
+ |
|
| 436 |
+ params := fmt.Sprintf("%s %d", poolName, deviceId)
|
|
| 437 |
+ if err := task.AddTarget(0, size/512, "thin", params); err != nil {
|
|
| 438 |
+ return fmt.Errorf("Can't add target")
|
|
| 439 |
+ } |
|
| 440 |
+ if err := task.SetAddNode(AddNodeOnCreate); err != nil {
|
|
| 441 |
+ return fmt.Errorf("Can't add node")
|
|
| 442 |
+ } |
|
| 443 |
+ |
|
| 444 |
+ var cookie uint = 0 |
|
| 445 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 446 |
+ return fmt.Errorf("Can't set cookie")
|
|
| 447 |
+ } |
|
| 448 |
+ |
|
| 449 |
+ if err := task.Run(); err != nil {
|
|
| 450 |
+ return fmt.Errorf("Error running DeviceCreate (activateDevice)")
|
|
| 451 |
+ } |
|
| 452 |
+ |
|
| 453 |
+ UdevWait(cookie) |
|
| 454 |
+ |
|
| 455 |
+ return nil |
|
| 456 |
+} |
|
| 457 |
+ |
|
| 458 |
+func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
|
|
| 459 |
+ devinfo, _ := getInfo(baseName) |
|
| 460 |
+ doSuspend := devinfo != nil && devinfo.Exists != 0 |
|
| 461 |
+ |
|
| 462 |
+ if doSuspend {
|
|
| 463 |
+ if err := suspendDevice(baseName); err != nil {
|
|
| 464 |
+ return err |
|
| 465 |
+ } |
|
| 466 |
+ } |
|
| 467 |
+ |
|
| 468 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 469 |
+ if task == nil {
|
|
| 470 |
+ if doSuspend {
|
|
| 471 |
+ resumeDevice(baseName) |
|
| 472 |
+ } |
|
| 473 |
+ return err |
|
| 474 |
+ } |
|
| 475 |
+ |
|
| 476 |
+ if err := task.SetSector(0); err != nil {
|
|
| 477 |
+ if doSuspend {
|
|
| 478 |
+ resumeDevice(baseName) |
|
| 479 |
+ } |
|
| 480 |
+ return fmt.Errorf("Can't set sector")
|
|
| 481 |
+ } |
|
| 482 |
+ |
|
| 483 |
+ if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
|
|
| 484 |
+ if doSuspend {
|
|
| 485 |
+ resumeDevice(baseName) |
|
| 486 |
+ } |
|
| 487 |
+ return fmt.Errorf("Can't set message")
|
|
| 488 |
+ } |
|
| 489 |
+ |
|
| 490 |
+ if err := task.Run(); err != nil {
|
|
| 491 |
+ if doSuspend {
|
|
| 492 |
+ resumeDevice(baseName) |
|
| 493 |
+ } |
|
| 494 |
+ return fmt.Errorf("Error running DeviceCreate (createSnapDevice)")
|
|
| 495 |
+ } |
|
| 496 |
+ |
|
| 497 |
+ if doSuspend {
|
|
| 498 |
+ if err := resumeDevice(baseName); err != nil {
|
|
| 499 |
+ return err |
|
| 500 |
+ } |
|
| 501 |
+ } |
|
| 502 |
+ |
|
| 503 |
+ return nil |
|
| 504 |
+} |
| 0 | 505 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,13 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+import "C" |
|
| 3 |
+ |
|
| 4 |
+// Due to the way cgo works this has to be in a separate file, as devmapper.go has |
|
| 5 |
+// definitions in the cgo block, which is incompatible with using "//export" |
|
| 6 |
+ |
|
| 7 |
+//export DevmapperLogCallback |
|
| 8 |
+func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_class C.int, message *C.char) {
|
|
| 9 |
+ if dmLogger != nil {
|
|
| 10 |
+ dmLogger.log(int(level), C.GoString(file), int(line), int(dm_errno_or_class), C.GoString(message)) |
|
| 11 |
+ } |
|
| 12 |
+} |
| 0 | 13 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,285 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "syscall" |
|
| 4 |
+ "testing" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+func TestTaskCreate(t *testing.T) {
|
|
| 8 |
+ // Test success |
|
| 9 |
+ taskCreate(t, DeviceInfo) |
|
| 10 |
+ |
|
| 11 |
+ // Test Failure |
|
| 12 |
+ DmTaskCreate = dmTaskCreateFail |
|
| 13 |
+ defer func() { DmTaskCreate = dmTaskCreateFct }()
|
|
| 14 |
+ if task := TaskCreate(-1); task != nil {
|
|
| 15 |
+ t.Fatalf("An error should have occured while creating an invalid task.")
|
|
| 16 |
+ } |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func TestTaskRun(t *testing.T) {
|
|
| 20 |
+ task := taskCreate(t, DeviceInfo) |
|
| 21 |
+ |
|
| 22 |
+ // Test success |
|
| 23 |
+ // Perform the RUN |
|
| 24 |
+ if err := task.Run(); err != nil {
|
|
| 25 |
+ t.Fatal(err) |
|
| 26 |
+ } |
|
| 27 |
+ // Make sure we don't have error with GetInfo |
|
| 28 |
+ if _, err := task.GetInfo(); err != nil {
|
|
| 29 |
+ t.Fatal(err) |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ // Test failure |
|
| 33 |
+ DmTaskRun = dmTaskRunFail |
|
| 34 |
+ defer func() { DmTaskRun = dmTaskRunFct }()
|
|
| 35 |
+ |
|
| 36 |
+ task = taskCreate(t, DeviceInfo) |
|
| 37 |
+ // Perform the RUN |
|
| 38 |
+ if err := task.Run(); err != ErrTaskRun {
|
|
| 39 |
+ t.Fatalf("An error should have occured while running task.")
|
|
| 40 |
+ } |
|
| 41 |
+ // Make sure GetInfo also fails |
|
| 42 |
+ if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 43 |
+ t.Fatalf("GetInfo should fail if task.Run() failed.")
|
|
| 44 |
+ } |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func TestTaskSetName(t *testing.T) {
|
|
| 48 |
+ task := taskCreate(t, DeviceInfo) |
|
| 49 |
+ |
|
| 50 |
+ // Test success |
|
| 51 |
+ if err := task.SetName("test"); err != nil {
|
|
| 52 |
+ t.Fatal(err) |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ // Test failure |
|
| 56 |
+ DmTaskSetName = dmTaskSetNameFail |
|
| 57 |
+ defer func() { DmTaskSetName = dmTaskSetNameFct }()
|
|
| 58 |
+ |
|
| 59 |
+ if err := task.SetName("test"); err != ErrTaskSetName {
|
|
| 60 |
+ t.Fatalf("An error should have occured while runnign SetName.")
|
|
| 61 |
+ } |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+func TestTaskSetMessage(t *testing.T) {
|
|
| 65 |
+ task := taskCreate(t, DeviceInfo) |
|
| 66 |
+ |
|
| 67 |
+ // Test success |
|
| 68 |
+ if err := task.SetMessage("test"); err != nil {
|
|
| 69 |
+ t.Fatal(err) |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ // Test failure |
|
| 73 |
+ DmTaskSetMessage = dmTaskSetMessageFail |
|
| 74 |
+ defer func() { DmTaskSetMessage = dmTaskSetMessageFct }()
|
|
| 75 |
+ |
|
| 76 |
+ if err := task.SetMessage("test"); err != ErrTaskSetMessage {
|
|
| 77 |
+ t.Fatalf("An error should have occured while runnign SetMessage.")
|
|
| 78 |
+ } |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func TestTaskSetSector(t *testing.T) {
|
|
| 82 |
+ task := taskCreate(t, DeviceInfo) |
|
| 83 |
+ |
|
| 84 |
+ // Test success |
|
| 85 |
+ if err := task.SetSector(128); err != nil {
|
|
| 86 |
+ t.Fatal(err) |
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ DmTaskSetSector = dmTaskSetSectorFail |
|
| 90 |
+ defer func() { DmTaskSetSector = dmTaskSetSectorFct }()
|
|
| 91 |
+ |
|
| 92 |
+ // Test failure |
|
| 93 |
+ if err := task.SetSector(0); err != ErrTaskSetSector {
|
|
| 94 |
+ t.Fatalf("An error should have occured while running SetSector.")
|
|
| 95 |
+ } |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+func TestTaskSetCookie(t *testing.T) {
|
|
| 99 |
+ var ( |
|
| 100 |
+ cookie uint = 0 |
|
| 101 |
+ task = taskCreate(t, DeviceInfo) |
|
| 102 |
+ ) |
|
| 103 |
+ |
|
| 104 |
+ // Test success |
|
| 105 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 106 |
+ t.Fatal(err) |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ // Test failure |
|
| 110 |
+ if err := task.SetCookie(nil, 0); err != ErrNilCookie {
|
|
| 111 |
+ t.Fatalf("An error should have occured while running SetCookie with nil cookie.")
|
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 114 |
+ DmTaskSetCookie = dmTaskSetCookieFail |
|
| 115 |
+ defer func() { DmTaskSetCookie = dmTaskSetCookieFct }()
|
|
| 116 |
+ |
|
| 117 |
+ if err := task.SetCookie(&cookie, 0); err != ErrTaskSetCookie {
|
|
| 118 |
+ t.Fatalf("An error should have occured while running SetCookie.")
|
|
| 119 |
+ } |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func TestTaskSetAddNode(t *testing.T) {
|
|
| 123 |
+ task := taskCreate(t, DeviceInfo) |
|
| 124 |
+ |
|
| 125 |
+ // Test success |
|
| 126 |
+ if err := task.SetAddNode(0); err != nil {
|
|
| 127 |
+ t.Fatal(err) |
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ // Test failure |
|
| 131 |
+ if err := task.SetAddNode(-1); err != ErrInvalidAddNode {
|
|
| 132 |
+ t.Fatalf("An error should have occured running SetAddNode with wrong node.")
|
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ DmTaskSetAddNode = dmTaskSetAddNodeFail |
|
| 136 |
+ defer func() { DmTaskSetAddNode = dmTaskSetAddNodeFct }()
|
|
| 137 |
+ |
|
| 138 |
+ if err := task.SetAddNode(0); err != ErrTaskSetAddNode {
|
|
| 139 |
+ t.Fatalf("An error should have occured running SetAddNode.")
|
|
| 140 |
+ } |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func TestTaskSetRo(t *testing.T) {
|
|
| 144 |
+ task := taskCreate(t, DeviceInfo) |
|
| 145 |
+ |
|
| 146 |
+ // Test success |
|
| 147 |
+ if err := task.SetRo(); err != nil {
|
|
| 148 |
+ t.Fatal(err) |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ // Test failure |
|
| 152 |
+ DmTaskSetRo = dmTaskSetRoFail |
|
| 153 |
+ defer func() { DmTaskSetRo = dmTaskSetRoFct }()
|
|
| 154 |
+ |
|
| 155 |
+ if err := task.SetRo(); err != ErrTaskSetRo {
|
|
| 156 |
+ t.Fatalf("An error should have occured running SetRo.")
|
|
| 157 |
+ } |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+func TestTaskAddTarget(t *testing.T) {
|
|
| 161 |
+ task := taskCreate(t, DeviceInfo) |
|
| 162 |
+ |
|
| 163 |
+ // Test success |
|
| 164 |
+ if err := task.AddTarget(0, 128, "thinp", ""); err != nil {
|
|
| 165 |
+ t.Fatal(err) |
|
| 166 |
+ } |
|
| 167 |
+ |
|
| 168 |
+ // Test failure |
|
| 169 |
+ DmTaskAddTarget = dmTaskAddTargetFail |
|
| 170 |
+ defer func() { DmTaskAddTarget = dmTaskAddTargetFct }()
|
|
| 171 |
+ |
|
| 172 |
+ if err := task.AddTarget(0, 128, "thinp", ""); err != ErrTaskAddTarget {
|
|
| 173 |
+ t.Fatalf("An error should have occured running AddTarget.")
|
|
| 174 |
+ } |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+// func TestTaskGetInfo(t *testing.T) {
|
|
| 178 |
+// task := taskCreate(t, DeviceInfo) |
|
| 179 |
+ |
|
| 180 |
+// // Test success |
|
| 181 |
+// if _, err := task.GetInfo(); err != nil {
|
|
| 182 |
+// t.Fatal(err) |
|
| 183 |
+// } |
|
| 184 |
+ |
|
| 185 |
+// // Test failure |
|
| 186 |
+// DmTaskGetInfo = dmTaskGetInfoFail |
|
| 187 |
+// defer func() { DmTaskGetInfo = dmTaskGetInfoFct }()
|
|
| 188 |
+ |
|
| 189 |
+// if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 190 |
+// t.Fatalf("An error should have occured running GetInfo.")
|
|
| 191 |
+// } |
|
| 192 |
+// } |
|
| 193 |
+ |
|
| 194 |
+// func TestTaskGetNextTarget(t *testing.T) {
|
|
| 195 |
+// task := taskCreate(t, DeviceInfo) |
|
| 196 |
+ |
|
| 197 |
+// if next, _, _, _, _ := task.GetNextTarget(0); next == 0 {
|
|
| 198 |
+// t.Fatalf("The next target should not be 0.")
|
|
| 199 |
+// } |
|
| 200 |
+// } |
|
| 201 |
+ |
|
| 202 |
+/// Utils |
|
| 203 |
+func taskCreate(t *testing.T, taskType TaskType) *Task {
|
|
| 204 |
+ task := TaskCreate(taskType) |
|
| 205 |
+ if task == nil {
|
|
| 206 |
+ t.Fatalf("Error creating task")
|
|
| 207 |
+ } |
|
| 208 |
+ return task |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+/// Failure function replacement |
|
| 212 |
+func dmTaskCreateFail(t int) *CDmTask {
|
|
| 213 |
+ return nil |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 216 |
+func dmTaskRunFail(task *CDmTask) int {
|
|
| 217 |
+ return -1 |
|
| 218 |
+} |
|
| 219 |
+ |
|
| 220 |
+func dmTaskSetNameFail(task *CDmTask, name string) int {
|
|
| 221 |
+ return -1 |
|
| 222 |
+} |
|
| 223 |
+ |
|
| 224 |
+func dmTaskSetMessageFail(task *CDmTask, message string) int {
|
|
| 225 |
+ return -1 |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+func dmTaskSetSectorFail(task *CDmTask, sector uint64) int {
|
|
| 229 |
+ return -1 |
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+func dmTaskSetCookieFail(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 233 |
+ return -1 |
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+func dmTaskSetAddNodeFail(task *CDmTask, addNode AddNodeType) int {
|
|
| 237 |
+ return -1 |
|
| 238 |
+} |
|
| 239 |
+ |
|
| 240 |
+func dmTaskSetRoFail(task *CDmTask) int {
|
|
| 241 |
+ return -1 |
|
| 242 |
+} |
|
| 243 |
+ |
|
| 244 |
+func dmTaskAddTargetFail(task *CDmTask, |
|
| 245 |
+ start, size uint64, ttype, params string) int {
|
|
| 246 |
+ return -1 |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+func dmTaskGetDriverVersionFail(task *CDmTask, version *string) int {
|
|
| 250 |
+ return -1 |
|
| 251 |
+} |
|
| 252 |
+ |
|
| 253 |
+func dmTaskGetInfoFail(task *CDmTask, info *Info) int {
|
|
| 254 |
+ return -1 |
|
| 255 |
+} |
|
| 256 |
+ |
|
| 257 |
+func dmGetNextTargetFail(task *CDmTask, next uintptr, start, length *uint64, |
|
| 258 |
+ target, params *string) uintptr {
|
|
| 259 |
+ return 0 |
|
| 260 |
+} |
|
| 261 |
+ |
|
| 262 |
+func dmAttachLoopDeviceFail(filename string, fd *int) string {
|
|
| 263 |
+ return "" |
|
| 264 |
+} |
|
| 265 |
+ |
|
| 266 |
+func sysGetBlockSizeFail(fd uintptr, size *uint64) syscall.Errno {
|
|
| 267 |
+ return 1 |
|
| 268 |
+} |
|
| 269 |
+ |
|
| 270 |
+func dmGetBlockSizeFail(fd uintptr) int64 {
|
|
| 271 |
+ return -1 |
|
| 272 |
+} |
|
| 273 |
+ |
|
| 274 |
+func dmUdevWaitFail(cookie uint) int {
|
|
| 275 |
+ return -1 |
|
| 276 |
+} |
|
| 277 |
+ |
|
| 278 |
+func dmSetDevDirFail(dir string) int {
|
|
| 279 |
+ return -1 |
|
| 280 |
+} |
|
| 281 |
+ |
|
| 282 |
+func dmGetLibraryVersionFail(version *string) int {
|
|
| 283 |
+ return -1 |
|
| 284 |
+} |
| 0 | 285 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,330 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+/* |
|
| 3 |
+#cgo LDFLAGS: -L. -ldevmapper |
|
| 4 |
+#include <stdio.h> |
|
| 5 |
+#include <stdlib.h> |
|
| 6 |
+#include <unistd.h> |
|
| 7 |
+#include <libdevmapper.h> |
|
| 8 |
+#include <linux/loop.h> |
|
| 9 |
+#include <sys/types.h> |
|
| 10 |
+#include <sys/stat.h> |
|
| 11 |
+#include <fcntl.h> |
|
| 12 |
+#include <sys/ioctl.h> |
|
| 13 |
+#include <linux/fs.h> |
|
| 14 |
+#include <errno.h> |
|
| 15 |
+ |
|
| 16 |
+#ifndef LOOP_CTL_GET_FREE |
|
| 17 |
+#define LOOP_CTL_GET_FREE 0x4C82 |
|
| 18 |
+#endif |
|
| 19 |
+ |
|
| 20 |
+// FIXME: this could easily be rewritten in go |
|
| 21 |
+char* attach_loop_device(const char *filename, int *loop_fd_out) |
|
| 22 |
+{
|
|
| 23 |
+ struct loop_info64 loopinfo = {0};
|
|
| 24 |
+ struct stat st; |
|
| 25 |
+ char buf[64]; |
|
| 26 |
+ int i, loop_fd, fd, start_index; |
|
| 27 |
+ char* loopname; |
|
| 28 |
+ |
|
| 29 |
+ |
|
| 30 |
+ *loop_fd_out = -1; |
|
| 31 |
+ |
|
| 32 |
+ start_index = 0; |
|
| 33 |
+ fd = open("/dev/loop-control", O_RDONLY);
|
|
| 34 |
+ if (fd >= 0) {
|
|
| 35 |
+ start_index = ioctl(fd, LOOP_CTL_GET_FREE); |
|
| 36 |
+ close(fd); |
|
| 37 |
+ |
|
| 38 |
+ if (start_index < 0) |
|
| 39 |
+ start_index = 0; |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ fd = open(filename, O_RDWR); |
|
| 43 |
+ if (fd < 0) {
|
|
| 44 |
+ perror("open");
|
|
| 45 |
+ return NULL; |
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ loop_fd = -1; |
|
| 49 |
+ for (i = start_index ; loop_fd < 0 ; i++ ) {
|
|
| 50 |
+ if (sprintf(buf, "/dev/loop%d", i) < 0) {
|
|
| 51 |
+ close(fd); |
|
| 52 |
+ return NULL; |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ if (stat(buf, &st)) {
|
|
| 56 |
+ if (!S_ISBLK(st.st_mode)) {
|
|
| 57 |
+ fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf); |
|
| 58 |
+ } else if (errno == ENOENT) {
|
|
| 59 |
+ fprintf(stderr, "[error] There are no more loopback device available.\n"); |
|
| 60 |
+ } else {
|
|
| 61 |
+ fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno); |
|
| 62 |
+ } |
|
| 63 |
+ close(fd); |
|
| 64 |
+ return NULL; |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ loop_fd = open(buf, O_RDWR); |
|
| 68 |
+ if (loop_fd < 0 && errno == ENOENT) {
|
|
| 69 |
+ fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf); |
|
| 70 |
+ close(fd); |
|
| 71 |
+ return NULL; |
|
| 72 |
+ } else if (loop_fd < 0) {
|
|
| 73 |
+ fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno); |
|
| 74 |
+ continue; |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) {
|
|
| 78 |
+ int errsv = errno; |
|
| 79 |
+ close(loop_fd); |
|
| 80 |
+ loop_fd = -1; |
|
| 81 |
+ if (errsv != EBUSY) {
|
|
| 82 |
+ close(fd); |
|
| 83 |
+ fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv)); |
|
| 84 |
+ return NULL; |
|
| 85 |
+ } |
|
| 86 |
+ continue; |
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ close(fd); |
|
| 90 |
+ |
|
| 91 |
+ strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE); |
|
| 92 |
+ loopinfo.lo_offset = 0; |
|
| 93 |
+ loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR; |
|
| 94 |
+ |
|
| 95 |
+ if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) {
|
|
| 96 |
+ perror("ioctl LOOP_SET_STATUS64");
|
|
| 97 |
+ if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
|
|
| 98 |
+ perror("ioctl LOOP_CLR_FD");
|
|
| 99 |
+ } |
|
| 100 |
+ close(loop_fd); |
|
| 101 |
+ fprintf (stderr, "cannot set up loopback device info"); |
|
| 102 |
+ return (NULL); |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ loopname = strdup(buf); |
|
| 106 |
+ if (loopname == NULL) {
|
|
| 107 |
+ close(loop_fd); |
|
| 108 |
+ return (NULL); |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ *loop_fd_out = loop_fd; |
|
| 112 |
+ return (loopname); |
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ return (NULL); |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); |
|
| 119 |
+ |
|
| 120 |
+static void log_cb(int level, const char *file, int line, |
|
| 121 |
+ int dm_errno_or_class, const char *f, ...) |
|
| 122 |
+{
|
|
| 123 |
+ char buffer[256]; |
|
| 124 |
+ va_list ap; |
|
| 125 |
+ |
|
| 126 |
+ va_start(ap, f); |
|
| 127 |
+ vsnprintf(buffer, 256, f, ap); |
|
| 128 |
+ va_end(ap); |
|
| 129 |
+ |
|
| 130 |
+ DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+static void log_with_errno_init() |
|
| 134 |
+{
|
|
| 135 |
+ dm_log_with_errno_init(log_cb); |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+*/ |
|
| 139 |
+import "C" |
|
| 140 |
+ |
|
| 141 |
+import ( |
|
| 142 |
+ "syscall" |
|
| 143 |
+ "unsafe" |
|
| 144 |
+) |
|
| 145 |
+ |
|
| 146 |
+type ( |
|
| 147 |
+ CDmTask C.struct_dm_task |
|
| 148 |
+) |
|
| 149 |
+ |
|
| 150 |
+var ( |
|
| 151 |
+ DmTaskDestory = dmTaskDestroyFct |
|
| 152 |
+ DmTaskCreate = dmTaskCreateFct |
|
| 153 |
+ DmTaskRun = dmTaskRunFct |
|
| 154 |
+ DmTaskSetName = dmTaskSetNameFct |
|
| 155 |
+ DmTaskSetMessage = dmTaskSetMessageFct |
|
| 156 |
+ DmTaskSetSector = dmTaskSetSectorFct |
|
| 157 |
+ DmTaskSetCookie = dmTaskSetCookieFct |
|
| 158 |
+ DmTaskSetAddNode = dmTaskSetAddNodeFct |
|
| 159 |
+ DmTaskSetRo = dmTaskSetRoFct |
|
| 160 |
+ DmTaskAddTarget = dmTaskAddTargetFct |
|
| 161 |
+ DmTaskGetInfo = dmTaskGetInfoFct |
|
| 162 |
+ DmGetNextTarget = dmGetNextTargetFct |
|
| 163 |
+ DmGetBlockSize = dmGetBlockSizeFct |
|
| 164 |
+ DmAttachLoopDevice = dmAttachLoopDeviceFct |
|
| 165 |
+ DmUdevWait = dmUdevWaitFct |
|
| 166 |
+ DmLogInitVerbose = dmLogInitVerboseFct |
|
| 167 |
+ DmSetDevDir = dmSetDevDirFct |
|
| 168 |
+ DmGetLibraryVersion = dmGetLibraryVersionFct |
|
| 169 |
+ LogWithErrnoInit = logWithErrnoInitFct |
|
| 170 |
+ GetBlockSize = getBlockSizeFct |
|
| 171 |
+) |
|
| 172 |
+ |
|
| 173 |
+func free(p *C.char) {
|
|
| 174 |
+ C.free(unsafe.Pointer(p)) |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func dmTaskDestroyFct(task *CDmTask) {
|
|
| 178 |
+ C.dm_task_destroy((*C.struct_dm_task)(task)) |
|
| 179 |
+} |
|
| 180 |
+ |
|
| 181 |
+func dmTaskCreateFct(taskType int) *CDmTask {
|
|
| 182 |
+ return (*CDmTask)(C.dm_task_create(C.int(taskType))) |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+func dmTaskRunFct(task *CDmTask) int {
|
|
| 186 |
+ return int(C.dm_task_run((*C.struct_dm_task)(task))) |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+func dmTaskSetNameFct(task *CDmTask, name string) int {
|
|
| 190 |
+ Cname := C.CString(name) |
|
| 191 |
+ defer free(Cname) |
|
| 192 |
+ |
|
| 193 |
+ return int(C.dm_task_set_name((*C.struct_dm_task)(task), |
|
| 194 |
+ Cname)) |
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+func dmTaskSetMessageFct(task *CDmTask, message string) int {
|
|
| 198 |
+ Cmessage := C.CString(message) |
|
| 199 |
+ defer free(Cmessage) |
|
| 200 |
+ |
|
| 201 |
+ return int(C.dm_task_set_message((*C.struct_dm_task)(task), |
|
| 202 |
+ Cmessage)) |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+func dmTaskSetSectorFct(task *CDmTask, sector uint64) int {
|
|
| 206 |
+ return int(C.dm_task_set_sector((*C.struct_dm_task)(task), |
|
| 207 |
+ C.uint64_t(sector))) |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 211 |
+ cCookie := C.uint32_t(*cookie) |
|
| 212 |
+ defer func() {
|
|
| 213 |
+ *cookie = uint(cCookie) |
|
| 214 |
+ }() |
|
| 215 |
+ return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, |
|
| 216 |
+ C.uint16_t(flags))) |
|
| 217 |
+} |
|
| 218 |
+ |
|
| 219 |
+func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int {
|
|
| 220 |
+ return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), |
|
| 221 |
+ C.dm_add_node_t(addNode))) |
|
| 222 |
+} |
|
| 223 |
+ |
|
| 224 |
+func dmTaskSetRoFct(task *CDmTask) int {
|
|
| 225 |
+ return int(C.dm_task_set_ro((*C.struct_dm_task)(task))) |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+func dmTaskAddTargetFct(task *CDmTask, |
|
| 229 |
+ start, size uint64, ttype, params string) int {
|
|
| 230 |
+ |
|
| 231 |
+ Cttype := C.CString(ttype) |
|
| 232 |
+ defer free(Cttype) |
|
| 233 |
+ |
|
| 234 |
+ Cparams := C.CString(params) |
|
| 235 |
+ defer free(Cparams) |
|
| 236 |
+ |
|
| 237 |
+ return int(C.dm_task_add_target((*C.struct_dm_task)(task), |
|
| 238 |
+ C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) |
|
| 239 |
+} |
|
| 240 |
+ |
|
| 241 |
+func dmGetBlockSizeFct(fd uintptr) (int64, syscall.Errno) {
|
|
| 242 |
+ var size int64 |
|
| 243 |
+ _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, |
|
| 244 |
+ uintptr(unsafe.Pointer(&size))) |
|
| 245 |
+ return size, err |
|
| 246 |
+} |
|
| 247 |
+ |
|
| 248 |
+func dmTaskGetInfoFct(task *CDmTask, info *Info) int {
|
|
| 249 |
+ Cinfo := C.struct_dm_info{}
|
|
| 250 |
+ defer func() {
|
|
| 251 |
+ info.Exists = int(Cinfo.exists) |
|
| 252 |
+ info.Suspended = int(Cinfo.suspended) |
|
| 253 |
+ info.LiveTable = int(Cinfo.live_table) |
|
| 254 |
+ info.InactiveTable = int(Cinfo.inactive_table) |
|
| 255 |
+ info.OpenCount = int32(Cinfo.open_count) |
|
| 256 |
+ info.EventNr = uint32(Cinfo.event_nr) |
|
| 257 |
+ info.Major = uint32(Cinfo.major) |
|
| 258 |
+ info.Minor = uint32(Cinfo.minor) |
|
| 259 |
+ info.ReadOnly = int(Cinfo.read_only) |
|
| 260 |
+ info.TargetCount = int32(Cinfo.target_count) |
|
| 261 |
+ }() |
|
| 262 |
+ return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) |
|
| 263 |
+} |
|
| 264 |
+ |
|
| 265 |
+func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, |
|
| 266 |
+ target, params *string) uintptr {
|
|
| 267 |
+ |
|
| 268 |
+ var ( |
|
| 269 |
+ Cstart, Clength C.uint64_t |
|
| 270 |
+ CtargetType, Cparams *C.char |
|
| 271 |
+ ) |
|
| 272 |
+ defer func() {
|
|
| 273 |
+ *start = uint64(Cstart) |
|
| 274 |
+ *length = uint64(Clength) |
|
| 275 |
+ *target = C.GoString(CtargetType) |
|
| 276 |
+ *params = C.GoString(Cparams) |
|
| 277 |
+ }() |
|
| 278 |
+ nextp := C.dm_get_next_target((*C.struct_dm_task)(task), |
|
| 279 |
+ unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) |
|
| 280 |
+ return uintptr(nextp) |
|
| 281 |
+} |
|
| 282 |
+ |
|
| 283 |
+func dmAttachLoopDeviceFct(filename string, fd *int) string {
|
|
| 284 |
+ cFilename := C.CString(filename) |
|
| 285 |
+ defer free(cFilename) |
|
| 286 |
+ |
|
| 287 |
+ var cFd C.int |
|
| 288 |
+ defer func() {
|
|
| 289 |
+ *fd = int(cFd) |
|
| 290 |
+ }() |
|
| 291 |
+ |
|
| 292 |
+ ret := C.attach_loop_device(cFilename, &cFd) |
|
| 293 |
+ defer free(ret) |
|
| 294 |
+ return C.GoString(ret) |
|
| 295 |
+} |
|
| 296 |
+ |
|
| 297 |
+func getBlockSizeFct(fd uintptr, size *uint64) syscall.Errno {
|
|
| 298 |
+ _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, |
|
| 299 |
+ uintptr(unsafe.Pointer(&size))) |
|
| 300 |
+ return err |
|
| 301 |
+} |
|
| 302 |
+ |
|
| 303 |
+func dmUdevWaitFct(cookie uint) int {
|
|
| 304 |
+ return int(C.dm_udev_wait(C.uint32_t(cookie))) |
|
| 305 |
+} |
|
| 306 |
+ |
|
| 307 |
+func dmLogInitVerboseFct(level int) {
|
|
| 308 |
+ C.dm_log_init_verbose(C.int(level)) |
|
| 309 |
+} |
|
| 310 |
+ |
|
| 311 |
+func logWithErrnoInitFct() {
|
|
| 312 |
+ C.log_with_errno_init() |
|
| 313 |
+} |
|
| 314 |
+ |
|
| 315 |
+func dmSetDevDirFct(dir string) int {
|
|
| 316 |
+ Cdir := C.CString(dir) |
|
| 317 |
+ defer free(Cdir) |
|
| 318 |
+ |
|
| 319 |
+ return int(C.dm_set_dev_dir(Cdir)) |
|
| 320 |
+} |
|
| 321 |
+ |
|
| 322 |
+func dmGetLibraryVersionFct(version *string) int {
|
|
| 323 |
+ buffer := C.CString(string(make([]byte, 128))) |
|
| 324 |
+ defer free(buffer) |
|
| 325 |
+ defer func() {
|
|
| 326 |
+ *version = C.GoString(buffer) |
|
| 327 |
+ }() |
|
| 328 |
+ return int(C.dm_get_library_version(buffer, 128)) |
|
| 329 |
+} |
| 0 | 330 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,62 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/devmapper" |
|
| 5 |
+ "os" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func usage() {
|
|
| 9 |
+ fmt.Printf("Usage: %s [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0])
|
|
| 10 |
+ os.Exit(1) |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func main() {
|
|
| 14 |
+ devices := devmapper.NewDeviceSet("/var/lib/docker")
|
|
| 15 |
+ |
|
| 16 |
+ if len(os.Args) < 2 {
|
|
| 17 |
+ usage() |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ cmd := os.Args[1] |
|
| 21 |
+ if cmd == "snap" {
|
|
| 22 |
+ if len(os.Args) < 4 {
|
|
| 23 |
+ usage() |
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ err := devices.AddDevice(os.Args[2], os.Args[3]) |
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ fmt.Println("Can't create snap device: ", err)
|
|
| 29 |
+ os.Exit(1) |
|
| 30 |
+ } |
|
| 31 |
+ } else if cmd == "remove" {
|
|
| 32 |
+ if len(os.Args) < 3 {
|
|
| 33 |
+ usage() |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ err := devices.RemoveDevice(os.Args[2]) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ fmt.Println("Can't remove device: ", err)
|
|
| 39 |
+ os.Exit(1) |
|
| 40 |
+ } |
|
| 41 |
+ } else if cmd == "mount" {
|
|
| 42 |
+ if len(os.Args) < 4 {
|
|
| 43 |
+ usage() |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ err := devices.MountDevice(os.Args[2], os.Args[3]) |
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ fmt.Println("Can't create snap device: ", err)
|
|
| 49 |
+ os.Exit(1) |
|
| 50 |
+ } |
|
| 51 |
+ } else {
|
|
| 52 |
+ fmt.Printf("Unknown command %s\n", cmd)
|
|
| 53 |
+ if len(os.Args) < 4 {
|
|
| 54 |
+ usage() |
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ os.Exit(1) |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ return |
|
| 61 |
+} |
| 0 | 62 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,92 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/graphdriver" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func init() {
|
|
| 10 |
+ graphdriver.Register("devicemapper", Init)
|
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// Placeholder interfaces, to be replaced |
|
| 14 |
+// at integration. |
|
| 15 |
+ |
|
| 16 |
+// End of placeholder interfaces. |
|
| 17 |
+ |
|
| 18 |
+type Driver struct {
|
|
| 19 |
+ *DeviceSet |
|
| 20 |
+ home string |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func Init(home string) (graphdriver.Driver, error) {
|
|
| 24 |
+ deviceSet, err := NewDeviceSet(home) |
|
| 25 |
+ if err != nil {
|
|
| 26 |
+ return nil, err |
|
| 27 |
+ } |
|
| 28 |
+ d := &Driver{
|
|
| 29 |
+ DeviceSet: deviceSet, |
|
| 30 |
+ home: home, |
|
| 31 |
+ } |
|
| 32 |
+ return d, nil |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func (d *Driver) String() string {
|
|
| 36 |
+ return "devicemapper" |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func (d *Driver) Status() [][2]string {
|
|
| 40 |
+ s := d.DeviceSet.Status() |
|
| 41 |
+ |
|
| 42 |
+ status := [][2]string{
|
|
| 43 |
+ {"Pool Name", s.PoolName},
|
|
| 44 |
+ {"Data file", s.DataLoopback},
|
|
| 45 |
+ {"Metadata file", s.MetadataLoopback},
|
|
| 46 |
+ {"Data Space Used", fmt.Sprintf("%.1f Mb", float64(s.Data.Used)/(1024*1024))},
|
|
| 47 |
+ {"Data Space Total", fmt.Sprintf("%.1f Mb", float64(s.Data.Total)/(1024*1024))},
|
|
| 48 |
+ {"Metadata Space Used", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Used)/(1024*1024))},
|
|
| 49 |
+ {"Metadata Space Total", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Total)/(1024*1024))},
|
|
| 50 |
+ } |
|
| 51 |
+ return status |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func (d *Driver) Cleanup() error {
|
|
| 55 |
+ return d.DeviceSet.Shutdown() |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func (d *Driver) Create(id string, parent string) error {
|
|
| 59 |
+ return d.DeviceSet.AddDevice(id, parent) |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+func (d *Driver) Remove(id string) error {
|
|
| 63 |
+ return d.DeviceSet.RemoveDevice(id) |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+func (d *Driver) Get(id string) (string, error) {
|
|
| 67 |
+ mp := path.Join(d.home, "mnt", id) |
|
| 68 |
+ if err := d.mount(id, mp); err != nil {
|
|
| 69 |
+ return "", err |
|
| 70 |
+ } |
|
| 71 |
+ return mp, nil |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+func (d *Driver) Size(id string) (int64, error) {
|
|
| 75 |
+ return -1, fmt.Errorf("Not implemented")
|
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+func (d *Driver) mount(id, mountPoint string) error {
|
|
| 79 |
+ // Create the target directories if they don't exist |
|
| 80 |
+ if err := os.MkdirAll(mountPoint, 0755); err != nil && !os.IsExist(err) {
|
|
| 81 |
+ return err |
|
| 82 |
+ } |
|
| 83 |
+ // If mountpoint is already mounted, do nothing |
|
| 84 |
+ if mounted, err := Mounted(mountPoint); err != nil {
|
|
| 85 |
+ return fmt.Errorf("Error checking mountpoint: %s", err)
|
|
| 86 |
+ } else if mounted {
|
|
| 87 |
+ return nil |
|
| 88 |
+ } |
|
| 89 |
+ // Mount the device |
|
| 90 |
+ return d.DeviceSet.MountDevice(id, mountPoint, false) |
|
| 91 |
+} |
| 0 | 92 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,301 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io/ioutil" |
|
| 4 |
+ "os" |
|
| 5 |
+ "path" |
|
| 6 |
+ "testing" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func init() {
|
|
| 10 |
+ // Reduce the size the the base fs and loopback for the tests |
|
| 11 |
+ DefaultDataLoopbackSize = 300 * 1024 * 1024 |
|
| 12 |
+ DefaultMetaDataLoopbackSize = 200 * 1024 * 1024 |
|
| 13 |
+ DefaultBaseFsSize = 300 * 1024 * 1024 |
|
| 14 |
+ |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func mkTestDirectory(t *testing.T) string {
|
|
| 18 |
+ dir, err := ioutil.TempDir("", "docker-test-devmapper-")
|
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ t.Fatal(err) |
|
| 21 |
+ } |
|
| 22 |
+ return dir |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func newDriver(t *testing.T) *Driver {
|
|
| 26 |
+ home := mkTestDirectory(t) |
|
| 27 |
+ d, err := Init(home) |
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ t.Fatal(err) |
|
| 30 |
+ } |
|
| 31 |
+ return d.(*Driver) |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func cleanup(d *Driver) {
|
|
| 35 |
+ d.Cleanup() |
|
| 36 |
+ os.RemoveAll(d.home) |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func TestInit(t *testing.T) {
|
|
| 40 |
+ home := mkTestDirectory(t) |
|
| 41 |
+ defer os.RemoveAll(home) |
|
| 42 |
+ driver, err := Init(home) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ t.Fatal(err) |
|
| 45 |
+ } |
|
| 46 |
+ defer func() {
|
|
| 47 |
+ if err := driver.Cleanup(); err != nil {
|
|
| 48 |
+ t.Fatal(err) |
|
| 49 |
+ } |
|
| 50 |
+ }() |
|
| 51 |
+ |
|
| 52 |
+ id := "foo" |
|
| 53 |
+ if err := driver.Create(id, ""); err != nil {
|
|
| 54 |
+ t.Fatal(err) |
|
| 55 |
+ } |
|
| 56 |
+ dir, err := driver.Get(id) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ t.Fatal(err) |
|
| 59 |
+ } |
|
| 60 |
+ if st, err := os.Stat(dir); err != nil {
|
|
| 61 |
+ t.Fatal(err) |
|
| 62 |
+ } else if !st.IsDir() {
|
|
| 63 |
+ t.Fatalf("Get(%V) did not return a directory", id)
|
|
| 64 |
+ } |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+func TestDriverName(t *testing.T) {
|
|
| 68 |
+ d := newDriver(t) |
|
| 69 |
+ defer cleanup(d) |
|
| 70 |
+ |
|
| 71 |
+ if d.String() != "devicemapper" {
|
|
| 72 |
+ t.Fatalf("Expected driver name to be devicemapper got %s", d.String())
|
|
| 73 |
+ } |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func TestDriverCreate(t *testing.T) {
|
|
| 77 |
+ d := newDriver(t) |
|
| 78 |
+ defer cleanup(d) |
|
| 79 |
+ |
|
| 80 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 81 |
+ t.Fatal(err) |
|
| 82 |
+ } |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func TestDriverRemove(t *testing.T) {
|
|
| 86 |
+ d := newDriver(t) |
|
| 87 |
+ defer cleanup(d) |
|
| 88 |
+ |
|
| 89 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 90 |
+ t.Fatal(err) |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ if err := d.Remove("1"); err != nil {
|
|
| 94 |
+ t.Fatal(err) |
|
| 95 |
+ } |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+func TestCleanup(t *testing.T) {
|
|
| 99 |
+ d := newDriver(t) |
|
| 100 |
+ defer os.RemoveAll(d.home) |
|
| 101 |
+ |
|
| 102 |
+ mountPoints := make([]string, 2) |
|
| 103 |
+ |
|
| 104 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 105 |
+ t.Fatal(err) |
|
| 106 |
+ } |
|
| 107 |
+ // Mount the id |
|
| 108 |
+ p, err := d.Get("1")
|
|
| 109 |
+ if err != nil {
|
|
| 110 |
+ t.Fatal(err) |
|
| 111 |
+ } |
|
| 112 |
+ mountPoints[0] = p |
|
| 113 |
+ |
|
| 114 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 115 |
+ t.Fatal(err) |
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ p, err = d.Get("2")
|
|
| 119 |
+ if err != nil {
|
|
| 120 |
+ t.Fatal(err) |
|
| 121 |
+ } |
|
| 122 |
+ mountPoints[1] = p |
|
| 123 |
+ |
|
| 124 |
+ // Ensure that all the mount points are currently mounted |
|
| 125 |
+ for _, p := range mountPoints {
|
|
| 126 |
+ if mounted, err := Mounted(p); err != nil {
|
|
| 127 |
+ t.Fatal(err) |
|
| 128 |
+ } else if !mounted {
|
|
| 129 |
+ t.Fatalf("Expected %s to be mounted", p)
|
|
| 130 |
+ } |
|
| 131 |
+ } |
|
| 132 |
+ |
|
| 133 |
+ // Ensure that devices are active |
|
| 134 |
+ for _, p := range []string{"1", "2"} {
|
|
| 135 |
+ if !d.HasActivatedDevice(p) {
|
|
| 136 |
+ t.Fatalf("Expected %s to have an active device", p)
|
|
| 137 |
+ } |
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ if err := d.Cleanup(); err != nil {
|
|
| 141 |
+ t.Fatal(err) |
|
| 142 |
+ } |
|
| 143 |
+ |
|
| 144 |
+ // Ensure that all the mount points are no longer mounted |
|
| 145 |
+ for _, p := range mountPoints {
|
|
| 146 |
+ if mounted, err := Mounted(p); err != nil {
|
|
| 147 |
+ t.Fatal(err) |
|
| 148 |
+ } else if mounted {
|
|
| 149 |
+ t.Fatalf("Expected %s to not be mounted", p)
|
|
| 150 |
+ } |
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ // Ensure that devices are no longer activated |
|
| 154 |
+ for _, p := range []string{"1", "2"} {
|
|
| 155 |
+ if d.HasActivatedDevice(p) {
|
|
| 156 |
+ t.Fatalf("Expected %s not be an active device", p)
|
|
| 157 |
+ } |
|
| 158 |
+ } |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+func TestNotMounted(t *testing.T) {
|
|
| 162 |
+ d := newDriver(t) |
|
| 163 |
+ defer cleanup(d) |
|
| 164 |
+ |
|
| 165 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 166 |
+ t.Fatal(err) |
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 170 |
+ if err != nil {
|
|
| 171 |
+ t.Fatal(err) |
|
| 172 |
+ } |
|
| 173 |
+ if mounted {
|
|
| 174 |
+ t.Fatal("Id 1 should not be mounted")
|
|
| 175 |
+ } |
|
| 176 |
+} |
|
| 177 |
+ |
|
| 178 |
+func TestMounted(t *testing.T) {
|
|
| 179 |
+ d := newDriver(t) |
|
| 180 |
+ defer cleanup(d) |
|
| 181 |
+ |
|
| 182 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 183 |
+ t.Fatal(err) |
|
| 184 |
+ } |
|
| 185 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 186 |
+ t.Fatal(err) |
|
| 187 |
+ } |
|
| 188 |
+ |
|
| 189 |
+ mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 190 |
+ if err != nil {
|
|
| 191 |
+ t.Fatal(err) |
|
| 192 |
+ } |
|
| 193 |
+ if !mounted {
|
|
| 194 |
+ t.Fatal("Id 1 should be mounted")
|
|
| 195 |
+ } |
|
| 196 |
+} |
|
| 197 |
+ |
|
| 198 |
+func TestInitCleanedDriver(t *testing.T) {
|
|
| 199 |
+ d := newDriver(t) |
|
| 200 |
+ |
|
| 201 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 202 |
+ t.Fatal(err) |
|
| 203 |
+ } |
|
| 204 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 205 |
+ t.Fatal(err) |
|
| 206 |
+ } |
|
| 207 |
+ |
|
| 208 |
+ if err := d.Cleanup(); err != nil {
|
|
| 209 |
+ t.Fatal(err) |
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ driver, err := Init(d.home) |
|
| 213 |
+ if err != nil {
|
|
| 214 |
+ t.Fatal(err) |
|
| 215 |
+ } |
|
| 216 |
+ d = driver.(*Driver) |
|
| 217 |
+ defer cleanup(d) |
|
| 218 |
+ |
|
| 219 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 220 |
+ t.Fatal(err) |
|
| 221 |
+ } |
|
| 222 |
+} |
|
| 223 |
+ |
|
| 224 |
+func TestMountMountedDriver(t *testing.T) {
|
|
| 225 |
+ d := newDriver(t) |
|
| 226 |
+ defer cleanup(d) |
|
| 227 |
+ |
|
| 228 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 229 |
+ t.Fatal(err) |
|
| 230 |
+ } |
|
| 231 |
+ |
|
| 232 |
+ // Perform get on same id to ensure that it will |
|
| 233 |
+ // not be mounted twice |
|
| 234 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 235 |
+ t.Fatal(err) |
|
| 236 |
+ } |
|
| 237 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 238 |
+ t.Fatal(err) |
|
| 239 |
+ } |
|
| 240 |
+} |
|
| 241 |
+ |
|
| 242 |
+func TestGetReturnsValidDevice(t *testing.T) {
|
|
| 243 |
+ d := newDriver(t) |
|
| 244 |
+ defer cleanup(d) |
|
| 245 |
+ |
|
| 246 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 247 |
+ t.Fatal(err) |
|
| 248 |
+ } |
|
| 249 |
+ |
|
| 250 |
+ if !d.HasDevice("1") {
|
|
| 251 |
+ t.Fatalf("Expected id 1 to be in device set")
|
|
| 252 |
+ } |
|
| 253 |
+ |
|
| 254 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 255 |
+ t.Fatal(err) |
|
| 256 |
+ } |
|
| 257 |
+ |
|
| 258 |
+ if !d.HasActivatedDevice("1") {
|
|
| 259 |
+ t.Fatalf("Expected id 1 to be activated")
|
|
| 260 |
+ } |
|
| 261 |
+ |
|
| 262 |
+ if !d.HasInitializedDevice("1") {
|
|
| 263 |
+ t.Fatalf("Expected id 1 to be initialized")
|
|
| 264 |
+ } |
|
| 265 |
+} |
|
| 266 |
+ |
|
| 267 |
+func TestDriverGetSize(t *testing.T) {
|
|
| 268 |
+ t.Skipf("Size is currently not implemented")
|
|
| 269 |
+ |
|
| 270 |
+ d := newDriver(t) |
|
| 271 |
+ defer cleanup(d) |
|
| 272 |
+ |
|
| 273 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 274 |
+ t.Fatal(err) |
|
| 275 |
+ } |
|
| 276 |
+ |
|
| 277 |
+ mountPoint, err := d.Get("1")
|
|
| 278 |
+ if err != nil {
|
|
| 279 |
+ t.Fatal(err) |
|
| 280 |
+ } |
|
| 281 |
+ |
|
| 282 |
+ size := int64(1024) |
|
| 283 |
+ |
|
| 284 |
+ f, err := os.Create(path.Join(mountPoint, "test_file")) |
|
| 285 |
+ if err != nil {
|
|
| 286 |
+ t.Fatal(err) |
|
| 287 |
+ } |
|
| 288 |
+ if err := f.Truncate(size); err != nil {
|
|
| 289 |
+ t.Fatal(err) |
|
| 290 |
+ } |
|
| 291 |
+ f.Close() |
|
| 292 |
+ |
|
| 293 |
+ diffSize, err := d.Size("1")
|
|
| 294 |
+ if err != nil {
|
|
| 295 |
+ t.Fatal(err) |
|
| 296 |
+ } |
|
| 297 |
+ if diffSize != size {
|
|
| 298 |
+ t.Fatalf("Expected size %d got %d", size, diffSize)
|
|
| 299 |
+ } |
|
| 300 |
+} |
| 0 | 301 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "path/filepath" |
|
| 5 |
+ "syscall" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// FIXME: this is copy-pasted from the aufs driver. |
|
| 9 |
+// It should be moved into the core. |
|
| 10 |
+ |
|
| 11 |
+func Mounted(mountpoint string) (bool, error) {
|
|
| 12 |
+ mntpoint, err := os.Stat(mountpoint) |
|
| 13 |
+ if err != nil {
|
|
| 14 |
+ if os.IsNotExist(err) {
|
|
| 15 |
+ return false, nil |
|
| 16 |
+ } |
|
| 17 |
+ return false, err |
|
| 18 |
+ } |
|
| 19 |
+ parent, err := os.Stat(filepath.Join(mountpoint, "..")) |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ return false, err |
|
| 22 |
+ } |
|
| 23 |
+ mntpointSt := mntpoint.Sys().(*syscall.Stat_t) |
|
| 24 |
+ parentSt := parent.Sys().(*syscall.Stat_t) |
|
| 25 |
+ return mntpointSt.Dev != parentSt.Dev, nil |
|
| 26 |
+} |
| ... | ... |
@@ -6,10 +6,10 @@ import ( |
| 6 | 6 |
"database/sql" |
| 7 | 7 |
"fmt" |
| 8 | 8 |
"github.com/dotcloud/docker/archive" |
| 9 |
- _ "github.com/dotcloud/docker/aufs" |
|
| 10 |
- _ "github.com/dotcloud/docker/devmapper" |
|
| 11 | 9 |
"github.com/dotcloud/docker/gograph" |
| 12 | 10 |
"github.com/dotcloud/docker/graphdriver" |
| 11 |
+ _ "github.com/dotcloud/docker/graphdriver/aufs" |
|
| 12 |
+ _ "github.com/dotcloud/docker/graphdriver/devmapper" |
|
| 13 | 13 |
_ "github.com/dotcloud/docker/graphdriver/dummy" |
| 14 | 14 |
"github.com/dotcloud/docker/utils" |
| 15 | 15 |
"io" |