| ... | ... |
@@ -4,7 +4,7 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"github.com/dotcloud/docker/archive" |
| 6 | 6 |
"github.com/dotcloud/docker/dockerversion" |
| 7 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 7 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 8 | 8 |
"github.com/dotcloud/docker/image" |
| 9 | 9 |
"github.com/dotcloud/docker/runconfig" |
| 10 | 10 |
"github.com/dotcloud/docker/utils" |
| ... | ... |
@@ -2,8 +2,8 @@ package graph |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 6 |
- _ "github.com/dotcloud/docker/graphdriver/vfs" // import the vfs driver so it is used in the tests |
|
| 5 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 6 |
+ _ "github.com/dotcloud/docker/runtime/graphdriver/vfs" // import the vfs driver so it is used in the tests |
|
| 7 | 7 |
"github.com/dotcloud/docker/image" |
| 8 | 8 |
"github.com/dotcloud/docker/utils" |
| 9 | 9 |
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" |
| 10 | 10 |
deleted file mode 100644 |
| ... | ... |
@@ -1,401 +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 |
- mountpk "github.com/dotcloud/docker/pkg/mount" |
|
| 29 |
- "github.com/dotcloud/docker/utils" |
|
| 30 |
- "os" |
|
| 31 |
- "os/exec" |
|
| 32 |
- "path" |
|
| 33 |
- "strings" |
|
| 34 |
- "sync" |
|
| 35 |
-) |
|
| 36 |
- |
|
| 37 |
-var ( |
|
| 38 |
- ErrAufsNotSupported = fmt.Errorf("AUFS was not found in /proc/filesystems")
|
|
| 39 |
-) |
|
| 40 |
- |
|
| 41 |
-func init() {
|
|
| 42 |
- graphdriver.Register("aufs", Init)
|
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-type Driver struct {
|
|
| 46 |
- root string |
|
| 47 |
- sync.Mutex // Protects concurrent modification to active |
|
| 48 |
- active map[string]int |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-// New returns a new AUFS driver. |
|
| 52 |
-// An error is returned if AUFS is not supported. |
|
| 53 |
-func Init(root string) (graphdriver.Driver, error) {
|
|
| 54 |
- // Try to load the aufs kernel module |
|
| 55 |
- if err := supportsAufs(); err != nil {
|
|
| 56 |
- return nil, err |
|
| 57 |
- } |
|
| 58 |
- paths := []string{
|
|
| 59 |
- "mnt", |
|
| 60 |
- "diff", |
|
| 61 |
- "layers", |
|
| 62 |
- } |
|
| 63 |
- |
|
| 64 |
- a := &Driver{
|
|
| 65 |
- root: root, |
|
| 66 |
- active: make(map[string]int), |
|
| 67 |
- } |
|
| 68 |
- |
|
| 69 |
- // Create the root aufs driver dir and return |
|
| 70 |
- // if it already exists |
|
| 71 |
- // If not populate the dir structure |
|
| 72 |
- if err := os.MkdirAll(root, 0755); err != nil {
|
|
| 73 |
- if os.IsExist(err) {
|
|
| 74 |
- return a, nil |
|
| 75 |
- } |
|
| 76 |
- return nil, err |
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- for _, p := range paths {
|
|
| 80 |
- if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
|
|
| 81 |
- return nil, err |
|
| 82 |
- } |
|
| 83 |
- } |
|
| 84 |
- return a, nil |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-// Return a nil error if the kernel supports aufs |
|
| 88 |
-// We cannot modprobe because inside dind modprobe fails |
|
| 89 |
-// to run |
|
| 90 |
-func supportsAufs() error {
|
|
| 91 |
- // We can try to modprobe aufs first before looking at |
|
| 92 |
- // proc/filesystems for when aufs is supported |
|
| 93 |
- exec.Command("modprobe", "aufs").Run()
|
|
| 94 |
- |
|
| 95 |
- f, err := os.Open("/proc/filesystems")
|
|
| 96 |
- if err != nil {
|
|
| 97 |
- return err |
|
| 98 |
- } |
|
| 99 |
- defer f.Close() |
|
| 100 |
- |
|
| 101 |
- s := bufio.NewScanner(f) |
|
| 102 |
- for s.Scan() {
|
|
| 103 |
- if strings.Contains(s.Text(), "aufs") {
|
|
| 104 |
- return nil |
|
| 105 |
- } |
|
| 106 |
- } |
|
| 107 |
- return ErrAufsNotSupported |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-func (a Driver) rootPath() string {
|
|
| 111 |
- return a.root |
|
| 112 |
-} |
|
| 113 |
- |
|
| 114 |
-func (Driver) String() string {
|
|
| 115 |
- return "aufs" |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-func (a Driver) Status() [][2]string {
|
|
| 119 |
- ids, _ := loadIds(path.Join(a.rootPath(), "layers")) |
|
| 120 |
- return [][2]string{
|
|
| 121 |
- {"Root Dir", a.rootPath()},
|
|
| 122 |
- {"Dirs", fmt.Sprintf("%d", len(ids))},
|
|
| 123 |
- } |
|
| 124 |
-} |
|
| 125 |
- |
|
| 126 |
-// Exists returns true if the given id is registered with |
|
| 127 |
-// this driver |
|
| 128 |
-func (a Driver) Exists(id string) bool {
|
|
| 129 |
- if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
|
|
| 130 |
- return false |
|
| 131 |
- } |
|
| 132 |
- return true |
|
| 133 |
-} |
|
| 134 |
- |
|
| 135 |
-// Three folders are created for each id |
|
| 136 |
-// mnt, layers, and diff |
|
| 137 |
-func (a *Driver) Create(id, parent string) error {
|
|
| 138 |
- if err := a.createDirsFor(id); err != nil {
|
|
| 139 |
- return err |
|
| 140 |
- } |
|
| 141 |
- // Write the layers metadata |
|
| 142 |
- f, err := os.Create(path.Join(a.rootPath(), "layers", id)) |
|
| 143 |
- if err != nil {
|
|
| 144 |
- return err |
|
| 145 |
- } |
|
| 146 |
- defer f.Close() |
|
| 147 |
- |
|
| 148 |
- if parent != "" {
|
|
| 149 |
- ids, err := getParentIds(a.rootPath(), parent) |
|
| 150 |
- if err != nil {
|
|
| 151 |
- return err |
|
| 152 |
- } |
|
| 153 |
- |
|
| 154 |
- if _, err := fmt.Fprintln(f, parent); err != nil {
|
|
| 155 |
- return err |
|
| 156 |
- } |
|
| 157 |
- for _, i := range ids {
|
|
| 158 |
- if _, err := fmt.Fprintln(f, i); err != nil {
|
|
| 159 |
- return err |
|
| 160 |
- } |
|
| 161 |
- } |
|
| 162 |
- } |
|
| 163 |
- return nil |
|
| 164 |
-} |
|
| 165 |
- |
|
| 166 |
-func (a *Driver) createDirsFor(id string) error {
|
|
| 167 |
- paths := []string{
|
|
| 168 |
- "mnt", |
|
| 169 |
- "diff", |
|
| 170 |
- } |
|
| 171 |
- |
|
| 172 |
- for _, p := range paths {
|
|
| 173 |
- if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
|
|
| 174 |
- return err |
|
| 175 |
- } |
|
| 176 |
- } |
|
| 177 |
- return nil |
|
| 178 |
-} |
|
| 179 |
- |
|
| 180 |
-// Unmount and remove the dir information |
|
| 181 |
-func (a *Driver) Remove(id string) error {
|
|
| 182 |
- // Protect the a.active from concurrent access |
|
| 183 |
- a.Lock() |
|
| 184 |
- defer a.Unlock() |
|
| 185 |
- |
|
| 186 |
- if a.active[id] != 0 {
|
|
| 187 |
- utils.Errorf("Warning: removing active id %s\n", id)
|
|
| 188 |
- } |
|
| 189 |
- |
|
| 190 |
- // Make sure the dir is umounted first |
|
| 191 |
- if err := a.unmount(id); err != nil {
|
|
| 192 |
- return err |
|
| 193 |
- } |
|
| 194 |
- tmpDirs := []string{
|
|
| 195 |
- "mnt", |
|
| 196 |
- "diff", |
|
| 197 |
- } |
|
| 198 |
- |
|
| 199 |
- // Atomically remove each directory in turn by first moving it out of the |
|
| 200 |
- // way (so that docker doesn't find it anymore) before doing removal of |
|
| 201 |
- // the whole tree. |
|
| 202 |
- for _, p := range tmpDirs {
|
|
| 203 |
- |
|
| 204 |
- realPath := path.Join(a.rootPath(), p, id) |
|
| 205 |
- tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id))
|
|
| 206 |
- if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) {
|
|
| 207 |
- return err |
|
| 208 |
- } |
|
| 209 |
- defer os.RemoveAll(tmpPath) |
|
| 210 |
- } |
|
| 211 |
- |
|
| 212 |
- // Remove the layers file for the id |
|
| 213 |
- if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
|
|
| 214 |
- return err |
|
| 215 |
- } |
|
| 216 |
- return nil |
|
| 217 |
-} |
|
| 218 |
- |
|
| 219 |
-// Return the rootfs path for the id |
|
| 220 |
-// This will mount the dir at it's given path |
|
| 221 |
-func (a *Driver) Get(id string) (string, error) {
|
|
| 222 |
- ids, err := getParentIds(a.rootPath(), id) |
|
| 223 |
- if err != nil {
|
|
| 224 |
- if !os.IsNotExist(err) {
|
|
| 225 |
- return "", err |
|
| 226 |
- } |
|
| 227 |
- ids = []string{}
|
|
| 228 |
- } |
|
| 229 |
- |
|
| 230 |
- // Protect the a.active from concurrent access |
|
| 231 |
- a.Lock() |
|
| 232 |
- defer a.Unlock() |
|
| 233 |
- |
|
| 234 |
- count := a.active[id] |
|
| 235 |
- |
|
| 236 |
- // If a dir does not have a parent ( no layers )do not try to mount |
|
| 237 |
- // just return the diff path to the data |
|
| 238 |
- out := path.Join(a.rootPath(), "diff", id) |
|
| 239 |
- if len(ids) > 0 {
|
|
| 240 |
- out = path.Join(a.rootPath(), "mnt", id) |
|
| 241 |
- |
|
| 242 |
- if count == 0 {
|
|
| 243 |
- if err := a.mount(id); err != nil {
|
|
| 244 |
- return "", err |
|
| 245 |
- } |
|
| 246 |
- } |
|
| 247 |
- } |
|
| 248 |
- |
|
| 249 |
- a.active[id] = count + 1 |
|
| 250 |
- |
|
| 251 |
- return out, nil |
|
| 252 |
-} |
|
| 253 |
- |
|
| 254 |
-func (a *Driver) Put(id string) {
|
|
| 255 |
- // Protect the a.active from concurrent access |
|
| 256 |
- a.Lock() |
|
| 257 |
- defer a.Unlock() |
|
| 258 |
- |
|
| 259 |
- if count := a.active[id]; count > 1 {
|
|
| 260 |
- a.active[id] = count - 1 |
|
| 261 |
- } else {
|
|
| 262 |
- ids, _ := getParentIds(a.rootPath(), id) |
|
| 263 |
- // We only mounted if there are any parents |
|
| 264 |
- if ids != nil && len(ids) > 0 {
|
|
| 265 |
- a.unmount(id) |
|
| 266 |
- } |
|
| 267 |
- delete(a.active, id) |
|
| 268 |
- } |
|
| 269 |
-} |
|
| 270 |
- |
|
| 271 |
-// Returns an archive of the contents for the id |
|
| 272 |
-func (a *Driver) Diff(id string) (archive.Archive, error) {
|
|
| 273 |
- return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
|
| 274 |
- Compression: archive.Uncompressed, |
|
| 275 |
- }) |
|
| 276 |
-} |
|
| 277 |
- |
|
| 278 |
-func (a *Driver) ApplyDiff(id string, diff archive.ArchiveReader) error {
|
|
| 279 |
- return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil) |
|
| 280 |
-} |
|
| 281 |
- |
|
| 282 |
-// Returns the size of the contents for the id |
|
| 283 |
-func (a *Driver) DiffSize(id string) (int64, error) {
|
|
| 284 |
- return utils.TreeSize(path.Join(a.rootPath(), "diff", id)) |
|
| 285 |
-} |
|
| 286 |
- |
|
| 287 |
-func (a *Driver) Changes(id string) ([]archive.Change, error) {
|
|
| 288 |
- layers, err := a.getParentLayerPaths(id) |
|
| 289 |
- if err != nil {
|
|
| 290 |
- return nil, err |
|
| 291 |
- } |
|
| 292 |
- return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) |
|
| 293 |
-} |
|
| 294 |
- |
|
| 295 |
-func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
|
|
| 296 |
- parentIds, err := getParentIds(a.rootPath(), id) |
|
| 297 |
- if err != nil {
|
|
| 298 |
- return nil, err |
|
| 299 |
- } |
|
| 300 |
- if len(parentIds) == 0 {
|
|
| 301 |
- return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
|
|
| 302 |
- } |
|
| 303 |
- layers := make([]string, len(parentIds)) |
|
| 304 |
- |
|
| 305 |
- // Get the diff paths for all the parent ids |
|
| 306 |
- for i, p := range parentIds {
|
|
| 307 |
- layers[i] = path.Join(a.rootPath(), "diff", p) |
|
| 308 |
- } |
|
| 309 |
- return layers, nil |
|
| 310 |
-} |
|
| 311 |
- |
|
| 312 |
-func (a *Driver) mount(id string) error {
|
|
| 313 |
- // If the id is mounted or we get an error return |
|
| 314 |
- if mounted, err := a.mounted(id); err != nil || mounted {
|
|
| 315 |
- return err |
|
| 316 |
- } |
|
| 317 |
- |
|
| 318 |
- var ( |
|
| 319 |
- target = path.Join(a.rootPath(), "mnt", id) |
|
| 320 |
- rw = path.Join(a.rootPath(), "diff", id) |
|
| 321 |
- ) |
|
| 322 |
- |
|
| 323 |
- layers, err := a.getParentLayerPaths(id) |
|
| 324 |
- if err != nil {
|
|
| 325 |
- return err |
|
| 326 |
- } |
|
| 327 |
- |
|
| 328 |
- if err := a.aufsMount(layers, rw, target); err != nil {
|
|
| 329 |
- return err |
|
| 330 |
- } |
|
| 331 |
- return nil |
|
| 332 |
-} |
|
| 333 |
- |
|
| 334 |
-func (a *Driver) unmount(id string) error {
|
|
| 335 |
- if mounted, err := a.mounted(id); err != nil || !mounted {
|
|
| 336 |
- return err |
|
| 337 |
- } |
|
| 338 |
- target := path.Join(a.rootPath(), "mnt", id) |
|
| 339 |
- return Unmount(target) |
|
| 340 |
-} |
|
| 341 |
- |
|
| 342 |
-func (a *Driver) mounted(id string) (bool, error) {
|
|
| 343 |
- target := path.Join(a.rootPath(), "mnt", id) |
|
| 344 |
- return mountpk.Mounted(target) |
|
| 345 |
-} |
|
| 346 |
- |
|
| 347 |
-// During cleanup aufs needs to unmount all mountpoints |
|
| 348 |
-func (a *Driver) Cleanup() error {
|
|
| 349 |
- ids, err := loadIds(path.Join(a.rootPath(), "layers")) |
|
| 350 |
- if err != nil {
|
|
| 351 |
- return err |
|
| 352 |
- } |
|
| 353 |
- for _, id := range ids {
|
|
| 354 |
- if err := a.unmount(id); err != nil {
|
|
| 355 |
- utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
|
|
| 356 |
- } |
|
| 357 |
- } |
|
| 358 |
- return nil |
|
| 359 |
-} |
|
| 360 |
- |
|
| 361 |
-func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
|
|
| 362 |
- defer func() {
|
|
| 363 |
- if err != nil {
|
|
| 364 |
- Unmount(target) |
|
| 365 |
- } |
|
| 366 |
- }() |
|
| 367 |
- |
|
| 368 |
- if err = a.tryMount(ro, rw, target); err != nil {
|
|
| 369 |
- if err = a.mountRw(rw, target); err != nil {
|
|
| 370 |
- return |
|
| 371 |
- } |
|
| 372 |
- |
|
| 373 |
- for _, layer := range ro {
|
|
| 374 |
- branch := fmt.Sprintf("append:%s=ro+wh", layer)
|
|
| 375 |
- if err = mount("none", target, "aufs", MsRemount, branch); err != nil {
|
|
| 376 |
- return |
|
| 377 |
- } |
|
| 378 |
- } |
|
| 379 |
- } |
|
| 380 |
- return |
|
| 381 |
-} |
|
| 382 |
- |
|
| 383 |
-// Try to mount using the aufs fast path, if this fails then |
|
| 384 |
-// append ro layers. |
|
| 385 |
-func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
|
|
| 386 |
- var ( |
|
| 387 |
- rwBranch = fmt.Sprintf("%s=rw", rw)
|
|
| 388 |
- roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
|
|
| 389 |
- ) |
|
| 390 |
- return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
|
|
| 391 |
-} |
|
| 392 |
- |
|
| 393 |
-func (a *Driver) mountRw(rw, target string) error {
|
|
| 394 |
- return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
|
|
| 395 |
-} |
|
| 396 |
- |
|
| 397 |
-func rollbackMount(target string, err error) {
|
|
| 398 |
- if err != nil {
|
|
| 399 |
- Unmount(target) |
|
| 400 |
- } |
|
| 401 |
-} |
| 402 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,697 +0,0 @@ |
| 1 |
-package aufs |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "crypto/sha256" |
|
| 5 |
- "encoding/hex" |
|
| 6 |
- "fmt" |
|
| 7 |
- "github.com/dotcloud/docker/archive" |
|
| 8 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 9 |
- "io/ioutil" |
|
| 10 |
- "os" |
|
| 11 |
- "path" |
|
| 12 |
- "testing" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-var ( |
|
| 16 |
- tmp = path.Join(os.TempDir(), "aufs-tests", "aufs") |
|
| 17 |
-) |
|
| 18 |
- |
|
| 19 |
-func testInit(dir string, t *testing.T) graphdriver.Driver {
|
|
| 20 |
- d, err := Init(dir) |
|
| 21 |
- if err != nil {
|
|
| 22 |
- if err == ErrAufsNotSupported {
|
|
| 23 |
- t.Skip(err) |
|
| 24 |
- } else {
|
|
| 25 |
- t.Fatal(err) |
|
| 26 |
- } |
|
| 27 |
- } |
|
| 28 |
- return d |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-func newDriver(t *testing.T) *Driver {
|
|
| 32 |
- if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 33 |
- t.Fatal(err) |
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 |
- d := testInit(tmp, t) |
|
| 37 |
- return d.(*Driver) |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func TestNewDriver(t *testing.T) {
|
|
| 41 |
- if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 42 |
- t.Fatal(err) |
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- d := testInit(tmp, t) |
|
| 46 |
- defer os.RemoveAll(tmp) |
|
| 47 |
- if d == nil {
|
|
| 48 |
- t.Fatalf("Driver should not be nil")
|
|
| 49 |
- } |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-func TestAufsString(t *testing.T) {
|
|
| 53 |
- d := newDriver(t) |
|
| 54 |
- defer os.RemoveAll(tmp) |
|
| 55 |
- |
|
| 56 |
- if d.String() != "aufs" {
|
|
| 57 |
- t.Fatalf("Expected aufs got %s", d.String())
|
|
| 58 |
- } |
|
| 59 |
-} |
|
| 60 |
- |
|
| 61 |
-func TestCreateDirStructure(t *testing.T) {
|
|
| 62 |
- newDriver(t) |
|
| 63 |
- defer os.RemoveAll(tmp) |
|
| 64 |
- |
|
| 65 |
- paths := []string{
|
|
| 66 |
- "mnt", |
|
| 67 |
- "layers", |
|
| 68 |
- "diff", |
|
| 69 |
- } |
|
| 70 |
- |
|
| 71 |
- for _, p := range paths {
|
|
| 72 |
- if _, err := os.Stat(path.Join(tmp, p)); err != nil {
|
|
| 73 |
- t.Fatal(err) |
|
| 74 |
- } |
|
| 75 |
- } |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-// We should be able to create two drivers with the same dir structure |
|
| 79 |
-func TestNewDriverFromExistingDir(t *testing.T) {
|
|
| 80 |
- if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 81 |
- t.Fatal(err) |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- testInit(tmp, t) |
|
| 85 |
- testInit(tmp, t) |
|
| 86 |
- os.RemoveAll(tmp) |
|
| 87 |
-} |
|
| 88 |
- |
|
| 89 |
-func TestCreateNewDir(t *testing.T) {
|
|
| 90 |
- d := newDriver(t) |
|
| 91 |
- defer os.RemoveAll(tmp) |
|
| 92 |
- |
|
| 93 |
- if err := d.Create("1", ""); err != nil {
|
|
| 94 |
- t.Fatal(err) |
|
| 95 |
- } |
|
| 96 |
-} |
|
| 97 |
- |
|
| 98 |
-func TestCreateNewDirStructure(t *testing.T) {
|
|
| 99 |
- d := newDriver(t) |
|
| 100 |
- defer os.RemoveAll(tmp) |
|
| 101 |
- |
|
| 102 |
- if err := d.Create("1", ""); err != nil {
|
|
| 103 |
- t.Fatal(err) |
|
| 104 |
- } |
|
| 105 |
- |
|
| 106 |
- paths := []string{
|
|
| 107 |
- "mnt", |
|
| 108 |
- "diff", |
|
| 109 |
- "layers", |
|
| 110 |
- } |
|
| 111 |
- |
|
| 112 |
- for _, p := range paths {
|
|
| 113 |
- if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
|
|
| 114 |
- t.Fatal(err) |
|
| 115 |
- } |
|
| 116 |
- } |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 |
-func TestRemoveImage(t *testing.T) {
|
|
| 120 |
- d := newDriver(t) |
|
| 121 |
- defer os.RemoveAll(tmp) |
|
| 122 |
- |
|
| 123 |
- if err := d.Create("1", ""); err != nil {
|
|
| 124 |
- t.Fatal(err) |
|
| 125 |
- } |
|
| 126 |
- |
|
| 127 |
- if err := d.Remove("1"); err != nil {
|
|
| 128 |
- t.Fatal(err) |
|
| 129 |
- } |
|
| 130 |
- |
|
| 131 |
- paths := []string{
|
|
| 132 |
- "mnt", |
|
| 133 |
- "diff", |
|
| 134 |
- "layers", |
|
| 135 |
- } |
|
| 136 |
- |
|
| 137 |
- for _, p := range paths {
|
|
| 138 |
- if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
|
|
| 139 |
- t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
|
|
| 140 |
- } |
|
| 141 |
- } |
|
| 142 |
-} |
|
| 143 |
- |
|
| 144 |
-func TestGetWithoutParent(t *testing.T) {
|
|
| 145 |
- d := newDriver(t) |
|
| 146 |
- defer os.RemoveAll(tmp) |
|
| 147 |
- |
|
| 148 |
- if err := d.Create("1", ""); err != nil {
|
|
| 149 |
- t.Fatal(err) |
|
| 150 |
- } |
|
| 151 |
- |
|
| 152 |
- diffPath, err := d.Get("1")
|
|
| 153 |
- if err != nil {
|
|
| 154 |
- t.Fatal(err) |
|
| 155 |
- } |
|
| 156 |
- expected := path.Join(tmp, "diff", "1") |
|
| 157 |
- if diffPath != expected {
|
|
| 158 |
- t.Fatalf("Expected path %s got %s", expected, diffPath)
|
|
| 159 |
- } |
|
| 160 |
-} |
|
| 161 |
- |
|
| 162 |
-func TestCleanupWithNoDirs(t *testing.T) {
|
|
| 163 |
- d := newDriver(t) |
|
| 164 |
- defer os.RemoveAll(tmp) |
|
| 165 |
- |
|
| 166 |
- if err := d.Cleanup(); err != nil {
|
|
| 167 |
- t.Fatal(err) |
|
| 168 |
- } |
|
| 169 |
-} |
|
| 170 |
- |
|
| 171 |
-func TestCleanupWithDir(t *testing.T) {
|
|
| 172 |
- d := newDriver(t) |
|
| 173 |
- defer os.RemoveAll(tmp) |
|
| 174 |
- |
|
| 175 |
- if err := d.Create("1", ""); err != nil {
|
|
| 176 |
- t.Fatal(err) |
|
| 177 |
- } |
|
| 178 |
- |
|
| 179 |
- if err := d.Cleanup(); err != nil {
|
|
| 180 |
- t.Fatal(err) |
|
| 181 |
- } |
|
| 182 |
-} |
|
| 183 |
- |
|
| 184 |
-func TestMountedFalseResponse(t *testing.T) {
|
|
| 185 |
- d := newDriver(t) |
|
| 186 |
- defer os.RemoveAll(tmp) |
|
| 187 |
- |
|
| 188 |
- if err := d.Create("1", ""); err != nil {
|
|
| 189 |
- t.Fatal(err) |
|
| 190 |
- } |
|
| 191 |
- |
|
| 192 |
- response, err := d.mounted("1")
|
|
| 193 |
- if err != nil {
|
|
| 194 |
- t.Fatal(err) |
|
| 195 |
- } |
|
| 196 |
- |
|
| 197 |
- if response != false {
|
|
| 198 |
- t.Fatalf("Response if dir id 1 is mounted should be false")
|
|
| 199 |
- } |
|
| 200 |
-} |
|
| 201 |
- |
|
| 202 |
-func TestMountedTrueReponse(t *testing.T) {
|
|
| 203 |
- d := newDriver(t) |
|
| 204 |
- defer os.RemoveAll(tmp) |
|
| 205 |
- defer d.Cleanup() |
|
| 206 |
- |
|
| 207 |
- if err := d.Create("1", ""); err != nil {
|
|
| 208 |
- t.Fatal(err) |
|
| 209 |
- } |
|
| 210 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 211 |
- t.Fatal(err) |
|
| 212 |
- } |
|
| 213 |
- |
|
| 214 |
- _, err := d.Get("2")
|
|
| 215 |
- if err != nil {
|
|
| 216 |
- t.Fatal(err) |
|
| 217 |
- } |
|
| 218 |
- |
|
| 219 |
- response, err := d.mounted("2")
|
|
| 220 |
- if err != nil {
|
|
| 221 |
- t.Fatal(err) |
|
| 222 |
- } |
|
| 223 |
- |
|
| 224 |
- if response != true {
|
|
| 225 |
- t.Fatalf("Response if dir id 2 is mounted should be true")
|
|
| 226 |
- } |
|
| 227 |
-} |
|
| 228 |
- |
|
| 229 |
-func TestMountWithParent(t *testing.T) {
|
|
| 230 |
- d := newDriver(t) |
|
| 231 |
- defer os.RemoveAll(tmp) |
|
| 232 |
- |
|
| 233 |
- if err := d.Create("1", ""); err != nil {
|
|
| 234 |
- t.Fatal(err) |
|
| 235 |
- } |
|
| 236 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 237 |
- t.Fatal(err) |
|
| 238 |
- } |
|
| 239 |
- |
|
| 240 |
- defer func() {
|
|
| 241 |
- if err := d.Cleanup(); err != nil {
|
|
| 242 |
- t.Fatal(err) |
|
| 243 |
- } |
|
| 244 |
- }() |
|
| 245 |
- |
|
| 246 |
- mntPath, err := d.Get("2")
|
|
| 247 |
- if err != nil {
|
|
| 248 |
- t.Fatal(err) |
|
| 249 |
- } |
|
| 250 |
- if mntPath == "" {
|
|
| 251 |
- t.Fatal("mntPath should not be empty string")
|
|
| 252 |
- } |
|
| 253 |
- |
|
| 254 |
- expected := path.Join(tmp, "mnt", "2") |
|
| 255 |
- if mntPath != expected {
|
|
| 256 |
- t.Fatalf("Expected %s got %s", expected, mntPath)
|
|
| 257 |
- } |
|
| 258 |
-} |
|
| 259 |
- |
|
| 260 |
-func TestRemoveMountedDir(t *testing.T) {
|
|
| 261 |
- d := newDriver(t) |
|
| 262 |
- defer os.RemoveAll(tmp) |
|
| 263 |
- |
|
| 264 |
- if err := d.Create("1", ""); err != nil {
|
|
| 265 |
- t.Fatal(err) |
|
| 266 |
- } |
|
| 267 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 268 |
- t.Fatal(err) |
|
| 269 |
- } |
|
| 270 |
- |
|
| 271 |
- defer func() {
|
|
| 272 |
- if err := d.Cleanup(); err != nil {
|
|
| 273 |
- t.Fatal(err) |
|
| 274 |
- } |
|
| 275 |
- }() |
|
| 276 |
- |
|
| 277 |
- mntPath, err := d.Get("2")
|
|
| 278 |
- if err != nil {
|
|
| 279 |
- t.Fatal(err) |
|
| 280 |
- } |
|
| 281 |
- if mntPath == "" {
|
|
| 282 |
- t.Fatal("mntPath should not be empty string")
|
|
| 283 |
- } |
|
| 284 |
- |
|
| 285 |
- mounted, err := d.mounted("2")
|
|
| 286 |
- if err != nil {
|
|
| 287 |
- t.Fatal(err) |
|
| 288 |
- } |
|
| 289 |
- |
|
| 290 |
- if !mounted {
|
|
| 291 |
- t.Fatalf("Dir id 2 should be mounted")
|
|
| 292 |
- } |
|
| 293 |
- |
|
| 294 |
- if err := d.Remove("2"); err != nil {
|
|
| 295 |
- t.Fatal(err) |
|
| 296 |
- } |
|
| 297 |
-} |
|
| 298 |
- |
|
| 299 |
-func TestCreateWithInvalidParent(t *testing.T) {
|
|
| 300 |
- d := newDriver(t) |
|
| 301 |
- defer os.RemoveAll(tmp) |
|
| 302 |
- |
|
| 303 |
- if err := d.Create("1", "docker"); err == nil {
|
|
| 304 |
- t.Fatalf("Error should not be nil with parent does not exist")
|
|
| 305 |
- } |
|
| 306 |
-} |
|
| 307 |
- |
|
| 308 |
-func TestGetDiff(t *testing.T) {
|
|
| 309 |
- d := newDriver(t) |
|
| 310 |
- defer os.RemoveAll(tmp) |
|
| 311 |
- |
|
| 312 |
- if err := d.Create("1", ""); err != nil {
|
|
| 313 |
- t.Fatal(err) |
|
| 314 |
- } |
|
| 315 |
- |
|
| 316 |
- diffPath, err := d.Get("1")
|
|
| 317 |
- if err != nil {
|
|
| 318 |
- t.Fatal(err) |
|
| 319 |
- } |
|
| 320 |
- |
|
| 321 |
- // Add a file to the diff path with a fixed size |
|
| 322 |
- size := int64(1024) |
|
| 323 |
- |
|
| 324 |
- f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 325 |
- if err != nil {
|
|
| 326 |
- t.Fatal(err) |
|
| 327 |
- } |
|
| 328 |
- if err := f.Truncate(size); err != nil {
|
|
| 329 |
- t.Fatal(err) |
|
| 330 |
- } |
|
| 331 |
- f.Close() |
|
| 332 |
- |
|
| 333 |
- a, err := d.Diff("1")
|
|
| 334 |
- if err != nil {
|
|
| 335 |
- t.Fatal(err) |
|
| 336 |
- } |
|
| 337 |
- if a == nil {
|
|
| 338 |
- t.Fatalf("Archive should not be nil")
|
|
| 339 |
- } |
|
| 340 |
-} |
|
| 341 |
- |
|
| 342 |
-func TestChanges(t *testing.T) {
|
|
| 343 |
- d := newDriver(t) |
|
| 344 |
- defer os.RemoveAll(tmp) |
|
| 345 |
- |
|
| 346 |
- if err := d.Create("1", ""); err != nil {
|
|
| 347 |
- t.Fatal(err) |
|
| 348 |
- } |
|
| 349 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 350 |
- t.Fatal(err) |
|
| 351 |
- } |
|
| 352 |
- |
|
| 353 |
- defer func() {
|
|
| 354 |
- if err := d.Cleanup(); err != nil {
|
|
| 355 |
- t.Fatal(err) |
|
| 356 |
- } |
|
| 357 |
- }() |
|
| 358 |
- |
|
| 359 |
- mntPoint, err := d.Get("2")
|
|
| 360 |
- if err != nil {
|
|
| 361 |
- t.Fatal(err) |
|
| 362 |
- } |
|
| 363 |
- |
|
| 364 |
- // Create a file to save in the mountpoint |
|
| 365 |
- f, err := os.Create(path.Join(mntPoint, "test.txt")) |
|
| 366 |
- if err != nil {
|
|
| 367 |
- t.Fatal(err) |
|
| 368 |
- } |
|
| 369 |
- |
|
| 370 |
- if _, err := f.WriteString("testline"); err != nil {
|
|
| 371 |
- t.Fatal(err) |
|
| 372 |
- } |
|
| 373 |
- if err := f.Close(); err != nil {
|
|
| 374 |
- t.Fatal(err) |
|
| 375 |
- } |
|
| 376 |
- |
|
| 377 |
- changes, err := d.Changes("2")
|
|
| 378 |
- if err != nil {
|
|
| 379 |
- t.Fatal(err) |
|
| 380 |
- } |
|
| 381 |
- if len(changes) != 1 {
|
|
| 382 |
- t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 383 |
- } |
|
| 384 |
- change := changes[0] |
|
| 385 |
- |
|
| 386 |
- expectedPath := "/test.txt" |
|
| 387 |
- if change.Path != expectedPath {
|
|
| 388 |
- t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 389 |
- } |
|
| 390 |
- |
|
| 391 |
- if change.Kind != archive.ChangeAdd {
|
|
| 392 |
- t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 393 |
- } |
|
| 394 |
- |
|
| 395 |
- if err := d.Create("3", "2"); err != nil {
|
|
| 396 |
- t.Fatal(err) |
|
| 397 |
- } |
|
| 398 |
- mntPoint, err = d.Get("3")
|
|
| 399 |
- if err != nil {
|
|
| 400 |
- t.Fatal(err) |
|
| 401 |
- } |
|
| 402 |
- |
|
| 403 |
- // Create a file to save in the mountpoint |
|
| 404 |
- f, err = os.Create(path.Join(mntPoint, "test2.txt")) |
|
| 405 |
- if err != nil {
|
|
| 406 |
- t.Fatal(err) |
|
| 407 |
- } |
|
| 408 |
- |
|
| 409 |
- if _, err := f.WriteString("testline"); err != nil {
|
|
| 410 |
- t.Fatal(err) |
|
| 411 |
- } |
|
| 412 |
- if err := f.Close(); err != nil {
|
|
| 413 |
- t.Fatal(err) |
|
| 414 |
- } |
|
| 415 |
- |
|
| 416 |
- changes, err = d.Changes("3")
|
|
| 417 |
- if err != nil {
|
|
| 418 |
- t.Fatal(err) |
|
| 419 |
- } |
|
| 420 |
- |
|
| 421 |
- if len(changes) != 1 {
|
|
| 422 |
- t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 423 |
- } |
|
| 424 |
- change = changes[0] |
|
| 425 |
- |
|
| 426 |
- expectedPath = "/test2.txt" |
|
| 427 |
- if change.Path != expectedPath {
|
|
| 428 |
- t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 429 |
- } |
|
| 430 |
- |
|
| 431 |
- if change.Kind != archive.ChangeAdd {
|
|
| 432 |
- t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 433 |
- } |
|
| 434 |
-} |
|
| 435 |
- |
|
| 436 |
-func TestDiffSize(t *testing.T) {
|
|
| 437 |
- d := newDriver(t) |
|
| 438 |
- defer os.RemoveAll(tmp) |
|
| 439 |
- |
|
| 440 |
- if err := d.Create("1", ""); err != nil {
|
|
| 441 |
- t.Fatal(err) |
|
| 442 |
- } |
|
| 443 |
- |
|
| 444 |
- diffPath, err := d.Get("1")
|
|
| 445 |
- if err != nil {
|
|
| 446 |
- t.Fatal(err) |
|
| 447 |
- } |
|
| 448 |
- |
|
| 449 |
- // Add a file to the diff path with a fixed size |
|
| 450 |
- size := int64(1024) |
|
| 451 |
- |
|
| 452 |
- f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 453 |
- if err != nil {
|
|
| 454 |
- t.Fatal(err) |
|
| 455 |
- } |
|
| 456 |
- if err := f.Truncate(size); err != nil {
|
|
| 457 |
- t.Fatal(err) |
|
| 458 |
- } |
|
| 459 |
- s, err := f.Stat() |
|
| 460 |
- if err != nil {
|
|
| 461 |
- t.Fatal(err) |
|
| 462 |
- } |
|
| 463 |
- size = s.Size() |
|
| 464 |
- if err := f.Close(); err != nil {
|
|
| 465 |
- t.Fatal(err) |
|
| 466 |
- } |
|
| 467 |
- |
|
| 468 |
- diffSize, err := d.DiffSize("1")
|
|
| 469 |
- if err != nil {
|
|
| 470 |
- t.Fatal(err) |
|
| 471 |
- } |
|
| 472 |
- if diffSize != size {
|
|
| 473 |
- t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
| 474 |
- } |
|
| 475 |
-} |
|
| 476 |
- |
|
| 477 |
-func TestChildDiffSize(t *testing.T) {
|
|
| 478 |
- d := newDriver(t) |
|
| 479 |
- defer os.RemoveAll(tmp) |
|
| 480 |
- defer d.Cleanup() |
|
| 481 |
- |
|
| 482 |
- if err := d.Create("1", ""); err != nil {
|
|
| 483 |
- t.Fatal(err) |
|
| 484 |
- } |
|
| 485 |
- |
|
| 486 |
- diffPath, err := d.Get("1")
|
|
| 487 |
- if err != nil {
|
|
| 488 |
- t.Fatal(err) |
|
| 489 |
- } |
|
| 490 |
- |
|
| 491 |
- // Add a file to the diff path with a fixed size |
|
| 492 |
- size := int64(1024) |
|
| 493 |
- |
|
| 494 |
- f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 495 |
- if err != nil {
|
|
| 496 |
- t.Fatal(err) |
|
| 497 |
- } |
|
| 498 |
- if err := f.Truncate(size); err != nil {
|
|
| 499 |
- t.Fatal(err) |
|
| 500 |
- } |
|
| 501 |
- s, err := f.Stat() |
|
| 502 |
- if err != nil {
|
|
| 503 |
- t.Fatal(err) |
|
| 504 |
- } |
|
| 505 |
- size = s.Size() |
|
| 506 |
- if err := f.Close(); err != nil {
|
|
| 507 |
- t.Fatal(err) |
|
| 508 |
- } |
|
| 509 |
- |
|
| 510 |
- diffSize, err := d.DiffSize("1")
|
|
| 511 |
- if err != nil {
|
|
| 512 |
- t.Fatal(err) |
|
| 513 |
- } |
|
| 514 |
- if diffSize != size {
|
|
| 515 |
- t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
| 516 |
- } |
|
| 517 |
- |
|
| 518 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 519 |
- t.Fatal(err) |
|
| 520 |
- } |
|
| 521 |
- |
|
| 522 |
- diffSize, err = d.DiffSize("2")
|
|
| 523 |
- if err != nil {
|
|
| 524 |
- t.Fatal(err) |
|
| 525 |
- } |
|
| 526 |
- // The diff size for the child should be zero |
|
| 527 |
- if diffSize != 0 {
|
|
| 528 |
- t.Fatalf("Expected size to be %d got %d", 0, diffSize)
|
|
| 529 |
- } |
|
| 530 |
-} |
|
| 531 |
- |
|
| 532 |
-func TestExists(t *testing.T) {
|
|
| 533 |
- d := newDriver(t) |
|
| 534 |
- defer os.RemoveAll(tmp) |
|
| 535 |
- defer d.Cleanup() |
|
| 536 |
- |
|
| 537 |
- if err := d.Create("1", ""); err != nil {
|
|
| 538 |
- t.Fatal(err) |
|
| 539 |
- } |
|
| 540 |
- |
|
| 541 |
- if d.Exists("none") {
|
|
| 542 |
- t.Fatal("id name should not exist in the driver")
|
|
| 543 |
- } |
|
| 544 |
- |
|
| 545 |
- if !d.Exists("1") {
|
|
| 546 |
- t.Fatal("id 1 should exist in the driver")
|
|
| 547 |
- } |
|
| 548 |
-} |
|
| 549 |
- |
|
| 550 |
-func TestStatus(t *testing.T) {
|
|
| 551 |
- d := newDriver(t) |
|
| 552 |
- defer os.RemoveAll(tmp) |
|
| 553 |
- defer d.Cleanup() |
|
| 554 |
- |
|
| 555 |
- if err := d.Create("1", ""); err != nil {
|
|
| 556 |
- t.Fatal(err) |
|
| 557 |
- } |
|
| 558 |
- |
|
| 559 |
- status := d.Status() |
|
| 560 |
- if status == nil || len(status) == 0 {
|
|
| 561 |
- t.Fatal("Status should not be nil or empty")
|
|
| 562 |
- } |
|
| 563 |
- rootDir := status[0] |
|
| 564 |
- dirs := status[1] |
|
| 565 |
- if rootDir[0] != "Root Dir" {
|
|
| 566 |
- t.Fatalf("Expected Root Dir got %s", rootDir[0])
|
|
| 567 |
- } |
|
| 568 |
- if rootDir[1] != d.rootPath() {
|
|
| 569 |
- t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
|
|
| 570 |
- } |
|
| 571 |
- if dirs[0] != "Dirs" {
|
|
| 572 |
- t.Fatalf("Expected Dirs got %s", dirs[0])
|
|
| 573 |
- } |
|
| 574 |
- if dirs[1] != "1" {
|
|
| 575 |
- t.Fatalf("Expected 1 got %s", dirs[1])
|
|
| 576 |
- } |
|
| 577 |
-} |
|
| 578 |
- |
|
| 579 |
-func TestApplyDiff(t *testing.T) {
|
|
| 580 |
- d := newDriver(t) |
|
| 581 |
- defer os.RemoveAll(tmp) |
|
| 582 |
- defer d.Cleanup() |
|
| 583 |
- |
|
| 584 |
- if err := d.Create("1", ""); err != nil {
|
|
| 585 |
- t.Fatal(err) |
|
| 586 |
- } |
|
| 587 |
- |
|
| 588 |
- diffPath, err := d.Get("1")
|
|
| 589 |
- if err != nil {
|
|
| 590 |
- t.Fatal(err) |
|
| 591 |
- } |
|
| 592 |
- |
|
| 593 |
- // Add a file to the diff path with a fixed size |
|
| 594 |
- size := int64(1024) |
|
| 595 |
- |
|
| 596 |
- f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 597 |
- if err != nil {
|
|
| 598 |
- t.Fatal(err) |
|
| 599 |
- } |
|
| 600 |
- if err := f.Truncate(size); err != nil {
|
|
| 601 |
- t.Fatal(err) |
|
| 602 |
- } |
|
| 603 |
- f.Close() |
|
| 604 |
- |
|
| 605 |
- diff, err := d.Diff("1")
|
|
| 606 |
- if err != nil {
|
|
| 607 |
- t.Fatal(err) |
|
| 608 |
- } |
|
| 609 |
- |
|
| 610 |
- if err := d.Create("2", ""); err != nil {
|
|
| 611 |
- t.Fatal(err) |
|
| 612 |
- } |
|
| 613 |
- if err := d.Create("3", "2"); err != nil {
|
|
| 614 |
- t.Fatal(err) |
|
| 615 |
- } |
|
| 616 |
- |
|
| 617 |
- if err := d.ApplyDiff("3", diff); err != nil {
|
|
| 618 |
- t.Fatal(err) |
|
| 619 |
- } |
|
| 620 |
- |
|
| 621 |
- // Ensure that the file is in the mount point for id 3 |
|
| 622 |
- |
|
| 623 |
- mountPoint, err := d.Get("3")
|
|
| 624 |
- if err != nil {
|
|
| 625 |
- t.Fatal(err) |
|
| 626 |
- } |
|
| 627 |
- if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
|
|
| 628 |
- t.Fatal(err) |
|
| 629 |
- } |
|
| 630 |
-} |
|
| 631 |
- |
|
| 632 |
-func hash(c string) string {
|
|
| 633 |
- h := sha256.New() |
|
| 634 |
- fmt.Fprint(h, c) |
|
| 635 |
- return hex.EncodeToString(h.Sum(nil)) |
|
| 636 |
-} |
|
| 637 |
- |
|
| 638 |
-func TestMountMoreThan42Layers(t *testing.T) {
|
|
| 639 |
- d := newDriver(t) |
|
| 640 |
- defer os.RemoveAll(tmp) |
|
| 641 |
- defer d.Cleanup() |
|
| 642 |
- var last string |
|
| 643 |
- var expected int |
|
| 644 |
- |
|
| 645 |
- for i := 1; i < 127; i++ {
|
|
| 646 |
- expected++ |
|
| 647 |
- var ( |
|
| 648 |
- parent = fmt.Sprintf("%d", i-1)
|
|
| 649 |
- current = fmt.Sprintf("%d", i)
|
|
| 650 |
- ) |
|
| 651 |
- |
|
| 652 |
- if parent == "0" {
|
|
| 653 |
- parent = "" |
|
| 654 |
- } else {
|
|
| 655 |
- parent = hash(parent) |
|
| 656 |
- } |
|
| 657 |
- current = hash(current) |
|
| 658 |
- |
|
| 659 |
- if err := d.Create(current, parent); err != nil {
|
|
| 660 |
- t.Logf("Current layer %d", i)
|
|
| 661 |
- t.Fatal(err) |
|
| 662 |
- } |
|
| 663 |
- point, err := d.Get(current) |
|
| 664 |
- if err != nil {
|
|
| 665 |
- t.Logf("Current layer %d", i)
|
|
| 666 |
- t.Fatal(err) |
|
| 667 |
- } |
|
| 668 |
- f, err := os.Create(path.Join(point, current)) |
|
| 669 |
- if err != nil {
|
|
| 670 |
- t.Logf("Current layer %d", i)
|
|
| 671 |
- t.Fatal(err) |
|
| 672 |
- } |
|
| 673 |
- f.Close() |
|
| 674 |
- |
|
| 675 |
- if i%10 == 0 {
|
|
| 676 |
- if err := os.Remove(path.Join(point, parent)); err != nil {
|
|
| 677 |
- t.Logf("Current layer %d", i)
|
|
| 678 |
- t.Fatal(err) |
|
| 679 |
- } |
|
| 680 |
- expected-- |
|
| 681 |
- } |
|
| 682 |
- last = current |
|
| 683 |
- } |
|
| 684 |
- |
|
| 685 |
- // Perform the actual mount for the top most image |
|
| 686 |
- point, err := d.Get(last) |
|
| 687 |
- if err != nil {
|
|
| 688 |
- t.Fatal(err) |
|
| 689 |
- } |
|
| 690 |
- files, err := ioutil.ReadDir(point) |
|
| 691 |
- if err != nil {
|
|
| 692 |
- t.Fatal(err) |
|
| 693 |
- } |
|
| 694 |
- if len(files) != expected {
|
|
| 695 |
- t.Fatalf("Expected %d got %d", expected, len(files))
|
|
| 696 |
- } |
|
| 697 |
-} |
| 698 | 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,194 +0,0 @@ |
| 1 |
-package aufs |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io/ioutil" |
|
| 7 |
- "os" |
|
| 8 |
- "path" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-type metadata struct {
|
|
| 12 |
- ID string `json:"id"` |
|
| 13 |
- ParentID string `json:"parent,omitempty"` |
|
| 14 |
- Image string `json:"Image,omitempty"` |
|
| 15 |
- |
|
| 16 |
- parent *metadata |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-func pathExists(pth string) bool {
|
|
| 20 |
- if _, err := os.Stat(pth); err != nil {
|
|
| 21 |
- return false |
|
| 22 |
- } |
|
| 23 |
- return true |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-// Migrate existing images and containers from docker < 0.7.x |
|
| 27 |
-// |
|
| 28 |
-// The format pre 0.7 is for docker to store the metadata and filesystem |
|
| 29 |
-// content in the same directory. For the migration to work we need to move Image layer |
|
| 30 |
-// data from /var/lib/docker/graph/<id>/layers to the diff of the registered id. |
|
| 31 |
-// |
|
| 32 |
-// Next we need to migrate the container's rw layer to diff of the driver. After the |
|
| 33 |
-// contents are migrated we need to register the image and container ids with the |
|
| 34 |
-// driver. |
|
| 35 |
-// |
|
| 36 |
-// For the migration we try to move the folder containing the layer files, if that |
|
| 37 |
-// fails because the data is currently mounted we will fallback to creating a |
|
| 38 |
-// symlink. |
|
| 39 |
-func (a *Driver) Migrate(pth string, setupInit func(p string) error) error {
|
|
| 40 |
- if pathExists(path.Join(pth, "graph")) {
|
|
| 41 |
- if err := a.migrateRepositories(pth); err != nil {
|
|
| 42 |
- return err |
|
| 43 |
- } |
|
| 44 |
- if err := a.migrateImages(path.Join(pth, "graph")); err != nil {
|
|
| 45 |
- return err |
|
| 46 |
- } |
|
| 47 |
- return a.migrateContainers(path.Join(pth, "containers"), setupInit) |
|
| 48 |
- } |
|
| 49 |
- return nil |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-func (a *Driver) migrateRepositories(pth string) error {
|
|
| 53 |
- name := path.Join(pth, "repositories") |
|
| 54 |
- if err := os.Rename(name, name+"-aufs"); err != nil && !os.IsNotExist(err) {
|
|
| 55 |
- return err |
|
| 56 |
- } |
|
| 57 |
- return nil |
|
| 58 |
-} |
|
| 59 |
- |
|
| 60 |
-func (a *Driver) migrateContainers(pth string, setupInit func(p string) error) error {
|
|
| 61 |
- fis, err := ioutil.ReadDir(pth) |
|
| 62 |
- if err != nil {
|
|
| 63 |
- return err |
|
| 64 |
- } |
|
| 65 |
- |
|
| 66 |
- for _, fi := range fis {
|
|
| 67 |
- if id := fi.Name(); fi.IsDir() && pathExists(path.Join(pth, id, "rw")) {
|
|
| 68 |
- if err := tryRelocate(path.Join(pth, id, "rw"), path.Join(a.rootPath(), "diff", id)); err != nil {
|
|
| 69 |
- return err |
|
| 70 |
- } |
|
| 71 |
- |
|
| 72 |
- if !a.Exists(id) {
|
|
| 73 |
- |
|
| 74 |
- metadata, err := loadMetadata(path.Join(pth, id, "config.json")) |
|
| 75 |
- if err != nil {
|
|
| 76 |
- return err |
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- initID := fmt.Sprintf("%s-init", id)
|
|
| 80 |
- if err := a.Create(initID, metadata.Image); err != nil {
|
|
| 81 |
- return err |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- initPath, err := a.Get(initID) |
|
| 85 |
- if err != nil {
|
|
| 86 |
- return err |
|
| 87 |
- } |
|
| 88 |
- // setup init layer |
|
| 89 |
- if err := setupInit(initPath); err != nil {
|
|
| 90 |
- return err |
|
| 91 |
- } |
|
| 92 |
- |
|
| 93 |
- if err := a.Create(id, initID); err != nil {
|
|
| 94 |
- return err |
|
| 95 |
- } |
|
| 96 |
- } |
|
| 97 |
- } |
|
| 98 |
- } |
|
| 99 |
- return nil |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-func (a *Driver) migrateImages(pth string) error {
|
|
| 103 |
- fis, err := ioutil.ReadDir(pth) |
|
| 104 |
- if err != nil {
|
|
| 105 |
- return err |
|
| 106 |
- } |
|
| 107 |
- var ( |
|
| 108 |
- m = make(map[string]*metadata) |
|
| 109 |
- current *metadata |
|
| 110 |
- exists bool |
|
| 111 |
- ) |
|
| 112 |
- |
|
| 113 |
- for _, fi := range fis {
|
|
| 114 |
- if id := fi.Name(); fi.IsDir() && pathExists(path.Join(pth, id, "layer")) {
|
|
| 115 |
- if current, exists = m[id]; !exists {
|
|
| 116 |
- current, err = loadMetadata(path.Join(pth, id, "json")) |
|
| 117 |
- if err != nil {
|
|
| 118 |
- return err |
|
| 119 |
- } |
|
| 120 |
- m[id] = current |
|
| 121 |
- } |
|
| 122 |
- } |
|
| 123 |
- } |
|
| 124 |
- |
|
| 125 |
- for _, v := range m {
|
|
| 126 |
- v.parent = m[v.ParentID] |
|
| 127 |
- } |
|
| 128 |
- |
|
| 129 |
- migrated := make(map[string]bool) |
|
| 130 |
- for _, v := range m {
|
|
| 131 |
- if err := a.migrateImage(v, pth, migrated); err != nil {
|
|
| 132 |
- return err |
|
| 133 |
- } |
|
| 134 |
- } |
|
| 135 |
- return nil |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-func (a *Driver) migrateImage(m *metadata, pth string, migrated map[string]bool) error {
|
|
| 139 |
- if !migrated[m.ID] {
|
|
| 140 |
- if m.parent != nil {
|
|
| 141 |
- a.migrateImage(m.parent, pth, migrated) |
|
| 142 |
- } |
|
| 143 |
- if err := tryRelocate(path.Join(pth, m.ID, "layer"), path.Join(a.rootPath(), "diff", m.ID)); err != nil {
|
|
| 144 |
- return err |
|
| 145 |
- } |
|
| 146 |
- if !a.Exists(m.ID) {
|
|
| 147 |
- if err := a.Create(m.ID, m.ParentID); err != nil {
|
|
| 148 |
- return err |
|
| 149 |
- } |
|
| 150 |
- } |
|
| 151 |
- migrated[m.ID] = true |
|
| 152 |
- } |
|
| 153 |
- return nil |
|
| 154 |
-} |
|
| 155 |
- |
|
| 156 |
-// tryRelocate will try to rename the old path to the new pack and if |
|
| 157 |
-// the operation fails, it will fallback to a symlink |
|
| 158 |
-func tryRelocate(oldPath, newPath string) error {
|
|
| 159 |
- s, err := os.Lstat(newPath) |
|
| 160 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 161 |
- return err |
|
| 162 |
- } |
|
| 163 |
- // If the destination is a symlink then we already tried to relocate once before |
|
| 164 |
- // and it failed so we delete it and try to remove |
|
| 165 |
- if s != nil && s.Mode()&os.ModeSymlink == os.ModeSymlink {
|
|
| 166 |
- if err := os.RemoveAll(newPath); err != nil {
|
|
| 167 |
- return err |
|
| 168 |
- } |
|
| 169 |
- } |
|
| 170 |
- if err := os.Rename(oldPath, newPath); err != nil {
|
|
| 171 |
- if sErr := os.Symlink(oldPath, newPath); sErr != nil {
|
|
| 172 |
- return fmt.Errorf("Unable to relocate %s to %s: Rename err %s Symlink err %s", oldPath, newPath, err, sErr)
|
|
| 173 |
- } |
|
| 174 |
- } |
|
| 175 |
- return nil |
|
| 176 |
-} |
|
| 177 |
- |
|
| 178 |
-func loadMetadata(pth string) (*metadata, error) {
|
|
| 179 |
- f, err := os.Open(pth) |
|
| 180 |
- if err != nil {
|
|
| 181 |
- return nil, err |
|
| 182 |
- } |
|
| 183 |
- defer f.Close() |
|
| 184 |
- |
|
| 185 |
- var ( |
|
| 186 |
- out = &metadata{}
|
|
| 187 |
- dec = json.NewDecoder(f) |
|
| 188 |
- ) |
|
| 189 |
- |
|
| 190 |
- if err := dec.Decode(out); err != nil {
|
|
| 191 |
- return nil, err |
|
| 192 |
- } |
|
| 193 |
- return out, nil |
|
| 194 |
-} |
| 195 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,17 +0,0 @@ |
| 1 |
-package aufs |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/utils" |
|
| 5 |
- "os/exec" |
|
| 6 |
- "syscall" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func Unmount(target string) error {
|
|
| 10 |
- if err := exec.Command("auplink", target, "flush").Run(); err != nil {
|
|
| 11 |
- utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
|
|
| 12 |
- } |
|
| 13 |
- if err := syscall.Unmount(target, 0); err != nil {
|
|
| 14 |
- return err |
|
| 15 |
- } |
|
| 16 |
- return nil |
|
| 17 |
-} |
| 18 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,11 +0,0 @@ |
| 1 |
-// +build amd64 |
|
| 2 |
- |
|
| 3 |
-package aufs |
|
| 4 |
- |
|
| 5 |
-import "syscall" |
|
| 6 |
- |
|
| 7 |
-const MsRemount = syscall.MS_REMOUNT |
|
| 8 |
- |
|
| 9 |
-func mount(source string, target string, fstype string, flags uintptr, data string) error {
|
|
| 10 |
- return syscall.Mount(source, target, fstype, flags, data) |
|
| 11 |
-} |
| 12 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,11 +0,0 @@ |
| 1 |
-// +build !linux !amd64 |
|
| 2 |
- |
|
| 3 |
-package aufs |
|
| 4 |
- |
|
| 5 |
-import "errors" |
|
| 6 |
- |
|
| 7 |
-const MsRemount = 0 |
|
| 8 |
- |
|
| 9 |
-func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
|
|
| 10 |
- return errors.New("mount is not implemented on darwin")
|
|
| 11 |
-} |
| 12 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,213 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package btrfs |
|
| 4 |
- |
|
| 5 |
-/* |
|
| 6 |
-#include <stdlib.h> |
|
| 7 |
-#include <dirent.h> |
|
| 8 |
-#include <btrfs/ioctl.h> |
|
| 9 |
-*/ |
|
| 10 |
-import "C" |
|
| 11 |
- |
|
| 12 |
-import ( |
|
| 13 |
- "fmt" |
|
| 14 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 15 |
- "os" |
|
| 16 |
- "path" |
|
| 17 |
- "syscall" |
|
| 18 |
- "unsafe" |
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-func init() {
|
|
| 22 |
- graphdriver.Register("btrfs", Init)
|
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 |
-func Init(home string) (graphdriver.Driver, error) {
|
|
| 26 |
- rootdir := path.Dir(home) |
|
| 27 |
- |
|
| 28 |
- var buf syscall.Statfs_t |
|
| 29 |
- if err := syscall.Statfs(rootdir, &buf); err != nil {
|
|
| 30 |
- return nil, err |
|
| 31 |
- } |
|
| 32 |
- |
|
| 33 |
- if buf.Type != 0x9123683E {
|
|
| 34 |
- return nil, fmt.Errorf("%s is not a btrfs filesystem", rootdir)
|
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- return &Driver{
|
|
| 38 |
- home: home, |
|
| 39 |
- }, nil |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 |
-type Driver struct {
|
|
| 43 |
- home string |
|
| 44 |
-} |
|
| 45 |
- |
|
| 46 |
-func (d *Driver) String() string {
|
|
| 47 |
- return "btrfs" |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func (d *Driver) Status() [][2]string {
|
|
| 51 |
- return nil |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-func (d *Driver) Cleanup() error {
|
|
| 55 |
- return nil |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-func free(p *C.char) {
|
|
| 59 |
- C.free(unsafe.Pointer(p)) |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-func openDir(path string) (*C.DIR, error) {
|
|
| 63 |
- Cpath := C.CString(path) |
|
| 64 |
- defer free(Cpath) |
|
| 65 |
- |
|
| 66 |
- dir := C.opendir(Cpath) |
|
| 67 |
- if dir == nil {
|
|
| 68 |
- return nil, fmt.Errorf("Can't open dir")
|
|
| 69 |
- } |
|
| 70 |
- return dir, nil |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-func closeDir(dir *C.DIR) {
|
|
| 74 |
- if dir != nil {
|
|
| 75 |
- C.closedir(dir) |
|
| 76 |
- } |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-func getDirFd(dir *C.DIR) uintptr {
|
|
| 80 |
- return uintptr(C.dirfd(dir)) |
|
| 81 |
-} |
|
| 82 |
- |
|
| 83 |
-func subvolCreate(path, name string) error {
|
|
| 84 |
- dir, err := openDir(path) |
|
| 85 |
- if err != nil {
|
|
| 86 |
- return err |
|
| 87 |
- } |
|
| 88 |
- defer closeDir(dir) |
|
| 89 |
- |
|
| 90 |
- var args C.struct_btrfs_ioctl_vol_args |
|
| 91 |
- for i, c := range []byte(name) {
|
|
| 92 |
- args.name[i] = C.char(c) |
|
| 93 |
- } |
|
| 94 |
- |
|
| 95 |
- _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE, |
|
| 96 |
- uintptr(unsafe.Pointer(&args))) |
|
| 97 |
- if errno != 0 {
|
|
| 98 |
- return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
|
|
| 99 |
- } |
|
| 100 |
- return nil |
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-func subvolSnapshot(src, dest, name string) error {
|
|
| 104 |
- srcDir, err := openDir(src) |
|
| 105 |
- if err != nil {
|
|
| 106 |
- return err |
|
| 107 |
- } |
|
| 108 |
- defer closeDir(srcDir) |
|
| 109 |
- |
|
| 110 |
- destDir, err := openDir(dest) |
|
| 111 |
- if err != nil {
|
|
| 112 |
- return err |
|
| 113 |
- } |
|
| 114 |
- defer closeDir(destDir) |
|
| 115 |
- |
|
| 116 |
- var args C.struct_btrfs_ioctl_vol_args_v2 |
|
| 117 |
- args.fd = C.__s64(getDirFd(srcDir)) |
|
| 118 |
- for i, c := range []byte(name) {
|
|
| 119 |
- args.name[i] = C.char(c) |
|
| 120 |
- } |
|
| 121 |
- |
|
| 122 |
- _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2, |
|
| 123 |
- uintptr(unsafe.Pointer(&args))) |
|
| 124 |
- if errno != 0 {
|
|
| 125 |
- return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
|
|
| 126 |
- } |
|
| 127 |
- return nil |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-func subvolDelete(path, name string) error {
|
|
| 131 |
- dir, err := openDir(path) |
|
| 132 |
- if err != nil {
|
|
| 133 |
- return err |
|
| 134 |
- } |
|
| 135 |
- defer closeDir(dir) |
|
| 136 |
- |
|
| 137 |
- var args C.struct_btrfs_ioctl_vol_args |
|
| 138 |
- for i, c := range []byte(name) {
|
|
| 139 |
- args.name[i] = C.char(c) |
|
| 140 |
- } |
|
| 141 |
- |
|
| 142 |
- _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY, |
|
| 143 |
- uintptr(unsafe.Pointer(&args))) |
|
| 144 |
- if errno != 0 {
|
|
| 145 |
- return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error())
|
|
| 146 |
- } |
|
| 147 |
- return nil |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-func (d *Driver) subvolumesDir() string {
|
|
| 151 |
- return path.Join(d.home, "subvolumes") |
|
| 152 |
-} |
|
| 153 |
- |
|
| 154 |
-func (d *Driver) subvolumesDirId(id string) string {
|
|
| 155 |
- return path.Join(d.subvolumesDir(), id) |
|
| 156 |
-} |
|
| 157 |
- |
|
| 158 |
-func (d *Driver) Create(id string, parent string) error {
|
|
| 159 |
- subvolumes := path.Join(d.home, "subvolumes") |
|
| 160 |
- if err := os.MkdirAll(subvolumes, 0700); err != nil {
|
|
| 161 |
- return err |
|
| 162 |
- } |
|
| 163 |
- if parent == "" {
|
|
| 164 |
- if err := subvolCreate(subvolumes, id); err != nil {
|
|
| 165 |
- return err |
|
| 166 |
- } |
|
| 167 |
- } else {
|
|
| 168 |
- parentDir, err := d.Get(parent) |
|
| 169 |
- if err != nil {
|
|
| 170 |
- return err |
|
| 171 |
- } |
|
| 172 |
- if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
|
|
| 173 |
- return err |
|
| 174 |
- } |
|
| 175 |
- } |
|
| 176 |
- return nil |
|
| 177 |
-} |
|
| 178 |
- |
|
| 179 |
-func (d *Driver) Remove(id string) error {
|
|
| 180 |
- dir := d.subvolumesDirId(id) |
|
| 181 |
- if _, err := os.Stat(dir); err != nil {
|
|
| 182 |
- return err |
|
| 183 |
- } |
|
| 184 |
- if err := subvolDelete(d.subvolumesDir(), id); err != nil {
|
|
| 185 |
- return err |
|
| 186 |
- } |
|
| 187 |
- return os.RemoveAll(dir) |
|
| 188 |
-} |
|
| 189 |
- |
|
| 190 |
-func (d *Driver) Get(id string) (string, error) {
|
|
| 191 |
- dir := d.subvolumesDirId(id) |
|
| 192 |
- st, err := os.Stat(dir) |
|
| 193 |
- if err != nil {
|
|
| 194 |
- return "", err |
|
| 195 |
- } |
|
| 196 |
- |
|
| 197 |
- if !st.IsDir() {
|
|
| 198 |
- return "", fmt.Errorf("%s: not a directory", dir)
|
|
| 199 |
- } |
|
| 200 |
- |
|
| 201 |
- return dir, nil |
|
| 202 |
-} |
|
| 203 |
- |
|
| 204 |
-func (d *Driver) Put(id string) {
|
|
| 205 |
- // Get() creates no runtime resources (like e.g. mounts) |
|
| 206 |
- // so this doesn't need to do anything. |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-func (d *Driver) Exists(id string) bool {
|
|
| 210 |
- dir := d.subvolumesDirId(id) |
|
| 211 |
- _, err := os.Stat(dir) |
|
| 212 |
- return err == nil |
|
| 213 |
-} |
| 4 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,126 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "fmt" |
|
| 7 |
- "github.com/dotcloud/docker/utils" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func stringToLoopName(src string) [LoNameSize]uint8 {
|
|
| 11 |
- var dst [LoNameSize]uint8 |
|
| 12 |
- copy(dst[:], src[:]) |
|
| 13 |
- return dst |
|
| 14 |
-} |
|
| 15 |
- |
|
| 16 |
-func getNextFreeLoopbackIndex() (int, error) {
|
|
| 17 |
- f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644)
|
|
| 18 |
- if err != nil {
|
|
| 19 |
- return 0, err |
|
| 20 |
- } |
|
| 21 |
- defer f.Close() |
|
| 22 |
- |
|
| 23 |
- index, err := ioctlLoopCtlGetFree(f.Fd()) |
|
| 24 |
- if index < 0 {
|
|
| 25 |
- index = 0 |
|
| 26 |
- } |
|
| 27 |
- return index, err |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 |
-func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, err error) {
|
|
| 31 |
- // Start looking for a free /dev/loop |
|
| 32 |
- for {
|
|
| 33 |
- target := fmt.Sprintf("/dev/loop%d", index)
|
|
| 34 |
- index++ |
|
| 35 |
- |
|
| 36 |
- fi, err := osStat(target) |
|
| 37 |
- if err != nil {
|
|
| 38 |
- if osIsNotExist(err) {
|
|
| 39 |
- utils.Errorf("There are no more loopback device available.")
|
|
| 40 |
- } |
|
| 41 |
- return nil, ErrAttachLoopbackDevice |
|
| 42 |
- } |
|
| 43 |
- |
|
| 44 |
- if fi.Mode()&osModeDevice != osModeDevice {
|
|
| 45 |
- utils.Errorf("Loopback device %s is not a block device.", target)
|
|
| 46 |
- continue |
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 |
- // OpenFile adds O_CLOEXEC |
|
| 50 |
- loopFile, err = osOpenFile(target, osORdWr, 0644) |
|
| 51 |
- if err != nil {
|
|
| 52 |
- utils.Errorf("Error openning loopback device: %s", err)
|
|
| 53 |
- return nil, ErrAttachLoopbackDevice |
|
| 54 |
- } |
|
| 55 |
- |
|
| 56 |
- // Try to attach to the loop file |
|
| 57 |
- if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil {
|
|
| 58 |
- loopFile.Close() |
|
| 59 |
- |
|
| 60 |
- // If the error is EBUSY, then try the next loopback |
|
| 61 |
- if err != sysEBusy {
|
|
| 62 |
- utils.Errorf("Cannot set up loopback device %s: %s", target, err)
|
|
| 63 |
- return nil, ErrAttachLoopbackDevice |
|
| 64 |
- } |
|
| 65 |
- |
|
| 66 |
- // Otherwise, we keep going with the loop |
|
| 67 |
- continue |
|
| 68 |
- } |
|
| 69 |
- // In case of success, we finished. Break the loop. |
|
| 70 |
- break |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- // This can't happen, but let's be sure |
|
| 74 |
- if loopFile == nil {
|
|
| 75 |
- utils.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
|
|
| 76 |
- return nil, ErrAttachLoopbackDevice |
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- return loopFile, nil |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-// attachLoopDevice attaches the given sparse file to the next |
|
| 83 |
-// available loopback device. It returns an opened *osFile. |
|
| 84 |
-func attachLoopDevice(sparseName string) (loop *osFile, err error) {
|
|
| 85 |
- |
|
| 86 |
- // Try to retrieve the next available loopback device via syscall. |
|
| 87 |
- // If it fails, we discard error and start loopking for a |
|
| 88 |
- // loopback from index 0. |
|
| 89 |
- startIndex, err := getNextFreeLoopbackIndex() |
|
| 90 |
- if err != nil {
|
|
| 91 |
- utils.Debugf("Error retrieving the next available loopback: %s", err)
|
|
| 92 |
- } |
|
| 93 |
- |
|
| 94 |
- // OpenFile adds O_CLOEXEC |
|
| 95 |
- sparseFile, err := osOpenFile(sparseName, osORdWr, 0644) |
|
| 96 |
- if err != nil {
|
|
| 97 |
- utils.Errorf("Error openning sparse file %s: %s", sparseName, err)
|
|
| 98 |
- return nil, ErrAttachLoopbackDevice |
|
| 99 |
- } |
|
| 100 |
- defer sparseFile.Close() |
|
| 101 |
- |
|
| 102 |
- loopFile, err := openNextAvailableLoopback(startIndex, sparseFile) |
|
| 103 |
- if err != nil {
|
|
| 104 |
- return nil, err |
|
| 105 |
- } |
|
| 106 |
- |
|
| 107 |
- // Set the status of the loopback device |
|
| 108 |
- loopInfo := &LoopInfo64{
|
|
| 109 |
- loFileName: stringToLoopName(loopFile.Name()), |
|
| 110 |
- loOffset: 0, |
|
| 111 |
- loFlags: LoFlagsAutoClear, |
|
| 112 |
- } |
|
| 113 |
- |
|
| 114 |
- if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil {
|
|
| 115 |
- utils.Errorf("Cannot set up loopback device info: %s", err)
|
|
| 116 |
- |
|
| 117 |
- // If the call failed, then free the loopback device |
|
| 118 |
- if err := ioctlLoopClrFd(loopFile.Fd()); err != nil {
|
|
| 119 |
- utils.Errorf("Error while cleaning up the loopback device")
|
|
| 120 |
- } |
|
| 121 |
- loopFile.Close() |
|
| 122 |
- return nil, ErrAttachLoopbackDevice |
|
| 123 |
- } |
|
| 124 |
- |
|
| 125 |
- return loopFile, nil |
|
| 126 |
-} |
| 127 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,1088 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "encoding/json" |
|
| 7 |
- "errors" |
|
| 8 |
- "fmt" |
|
| 9 |
- "github.com/dotcloud/docker/utils" |
|
| 10 |
- "io" |
|
| 11 |
- "io/ioutil" |
|
| 12 |
- "path" |
|
| 13 |
- "path/filepath" |
|
| 14 |
- "strconv" |
|
| 15 |
- "strings" |
|
| 16 |
- "sync" |
|
| 17 |
- "time" |
|
| 18 |
-) |
|
| 19 |
- |
|
| 20 |
-var ( |
|
| 21 |
- DefaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024 |
|
| 22 |
- DefaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024 |
|
| 23 |
- DefaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024 |
|
| 24 |
-) |
|
| 25 |
- |
|
| 26 |
-type DevInfo struct {
|
|
| 27 |
- Hash string `json:"-"` |
|
| 28 |
- DeviceId int `json:"device_id"` |
|
| 29 |
- Size uint64 `json:"size"` |
|
| 30 |
- TransactionId uint64 `json:"transaction_id"` |
|
| 31 |
- Initialized bool `json:"initialized"` |
|
| 32 |
- devices *DeviceSet `json:"-"` |
|
| 33 |
- |
|
| 34 |
- mountCount int `json:"-"` |
|
| 35 |
- mountPath string `json:"-"` |
|
| 36 |
- // A floating mount means one reference is not owned and |
|
| 37 |
- // will be stolen by the next mount. This allows us to |
|
| 38 |
- // avoid unmounting directly after creation before the |
|
| 39 |
- // first get (since we need to mount to set up the device |
|
| 40 |
- // a bit first). |
|
| 41 |
- floating bool `json:"-"` |
|
| 42 |
- |
|
| 43 |
- // The global DeviceSet lock guarantees that we serialize all |
|
| 44 |
- // the calls to libdevmapper (which is not threadsafe), but we |
|
| 45 |
- // sometimes release that lock while sleeping. In that case |
|
| 46 |
- // this per-device lock is still held, protecting against |
|
| 47 |
- // other accesses to the device that we're doing the wait on. |
|
| 48 |
- lock sync.Mutex `json:"-"` |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-type MetaData struct {
|
|
| 52 |
- Devices map[string]*DevInfo `json:devices` |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-type DeviceSet struct {
|
|
| 56 |
- MetaData |
|
| 57 |
- sync.Mutex // Protects Devices map and serializes calls into libdevmapper |
|
| 58 |
- root string |
|
| 59 |
- devicePrefix string |
|
| 60 |
- TransactionId uint64 |
|
| 61 |
- NewTransactionId uint64 |
|
| 62 |
- nextFreeDevice int |
|
| 63 |
- sawBusy bool |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-type DiskUsage struct {
|
|
| 67 |
- Used uint64 |
|
| 68 |
- Total uint64 |
|
| 69 |
-} |
|
| 70 |
- |
|
| 71 |
-type Status struct {
|
|
| 72 |
- PoolName string |
|
| 73 |
- DataLoopback string |
|
| 74 |
- MetadataLoopback string |
|
| 75 |
- Data DiskUsage |
|
| 76 |
- Metadata DiskUsage |
|
| 77 |
- SectorSize uint64 |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-type DevStatus struct {
|
|
| 81 |
- DeviceId int |
|
| 82 |
- Size uint64 |
|
| 83 |
- TransactionId uint64 |
|
| 84 |
- SizeInSectors uint64 |
|
| 85 |
- MappedSectors uint64 |
|
| 86 |
- HighestMappedSector uint64 |
|
| 87 |
-} |
|
| 88 |
- |
|
| 89 |
-type UnmountMode int |
|
| 90 |
- |
|
| 91 |
-const ( |
|
| 92 |
- UnmountRegular UnmountMode = iota |
|
| 93 |
- UnmountFloat |
|
| 94 |
- UnmountSink |
|
| 95 |
-) |
|
| 96 |
- |
|
| 97 |
-func getDevName(name string) string {
|
|
| 98 |
- return "/dev/mapper/" + name |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-func (info *DevInfo) Name() string {
|
|
| 102 |
- hash := info.Hash |
|
| 103 |
- if hash == "" {
|
|
| 104 |
- hash = "base" |
|
| 105 |
- } |
|
| 106 |
- return fmt.Sprintf("%s-%s", info.devices.devicePrefix, hash)
|
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-func (info *DevInfo) DevName() string {
|
|
| 110 |
- return getDevName(info.Name()) |
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-func (devices *DeviceSet) loopbackDir() string {
|
|
| 114 |
- return path.Join(devices.root, "devicemapper") |
|
| 115 |
-} |
|
| 116 |
- |
|
| 117 |
-func (devices *DeviceSet) jsonFile() string {
|
|
| 118 |
- return path.Join(devices.loopbackDir(), "json") |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 |
-func (devices *DeviceSet) getPoolName() string {
|
|
| 122 |
- return devices.devicePrefix + "-pool" |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-func (devices *DeviceSet) getPoolDevName() string {
|
|
| 126 |
- return getDevName(devices.getPoolName()) |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-func (devices *DeviceSet) hasImage(name string) bool {
|
|
| 130 |
- dirname := devices.loopbackDir() |
|
| 131 |
- filename := path.Join(dirname, name) |
|
| 132 |
- |
|
| 133 |
- _, err := osStat(filename) |
|
| 134 |
- return err == nil |
|
| 135 |
-} |
|
| 136 |
- |
|
| 137 |
-// ensureImage creates a sparse file of <size> bytes at the path |
|
| 138 |
-// <root>/devicemapper/<name>. |
|
| 139 |
-// If the file already exists, it does nothing. |
|
| 140 |
-// Either way it returns the full path. |
|
| 141 |
-func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
|
| 142 |
- dirname := devices.loopbackDir() |
|
| 143 |
- filename := path.Join(dirname, name) |
|
| 144 |
- |
|
| 145 |
- if err := osMkdirAll(dirname, 0700); err != nil && !osIsExist(err) {
|
|
| 146 |
- return "", err |
|
| 147 |
- } |
|
| 148 |
- |
|
| 149 |
- if _, err := osStat(filename); err != nil {
|
|
| 150 |
- if !osIsNotExist(err) {
|
|
| 151 |
- return "", err |
|
| 152 |
- } |
|
| 153 |
- utils.Debugf("Creating loopback file %s for device-manage use", filename)
|
|
| 154 |
- file, err := osOpenFile(filename, osORdWr|osOCreate, 0600) |
|
| 155 |
- if err != nil {
|
|
| 156 |
- return "", err |
|
| 157 |
- } |
|
| 158 |
- defer file.Close() |
|
| 159 |
- |
|
| 160 |
- if err = file.Truncate(size); err != nil {
|
|
| 161 |
- return "", err |
|
| 162 |
- } |
|
| 163 |
- } |
|
| 164 |
- return filename, nil |
|
| 165 |
-} |
|
| 166 |
- |
|
| 167 |
-func (devices *DeviceSet) allocateDeviceId() int {
|
|
| 168 |
- // TODO: Add smarter reuse of deleted devices |
|
| 169 |
- id := devices.nextFreeDevice |
|
| 170 |
- devices.nextFreeDevice = devices.nextFreeDevice + 1 |
|
| 171 |
- return id |
|
| 172 |
-} |
|
| 173 |
- |
|
| 174 |
-func (devices *DeviceSet) allocateTransactionId() uint64 {
|
|
| 175 |
- devices.NewTransactionId = devices.NewTransactionId + 1 |
|
| 176 |
- return devices.NewTransactionId |
|
| 177 |
-} |
|
| 178 |
- |
|
| 179 |
-func (devices *DeviceSet) saveMetadata() error {
|
|
| 180 |
- jsonData, err := json.Marshal(devices.MetaData) |
|
| 181 |
- if err != nil {
|
|
| 182 |
- return fmt.Errorf("Error encoding metadata to json: %s", err)
|
|
| 183 |
- } |
|
| 184 |
- tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json") |
|
| 185 |
- if err != nil {
|
|
| 186 |
- return fmt.Errorf("Error creating metadata file: %s", err)
|
|
| 187 |
- } |
|
| 188 |
- |
|
| 189 |
- n, err := tmpFile.Write(jsonData) |
|
| 190 |
- if err != nil {
|
|
| 191 |
- return fmt.Errorf("Error writing metadata to %s: %s", tmpFile.Name(), err)
|
|
| 192 |
- } |
|
| 193 |
- if n < len(jsonData) {
|
|
| 194 |
- return io.ErrShortWrite |
|
| 195 |
- } |
|
| 196 |
- if err := tmpFile.Sync(); err != nil {
|
|
| 197 |
- return fmt.Errorf("Error syncing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 198 |
- } |
|
| 199 |
- if err := tmpFile.Close(); err != nil {
|
|
| 200 |
- return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 201 |
- } |
|
| 202 |
- if err := osRename(tmpFile.Name(), devices.jsonFile()); err != nil {
|
|
| 203 |
- return fmt.Errorf("Error committing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 204 |
- } |
|
| 205 |
- |
|
| 206 |
- if devices.NewTransactionId != devices.TransactionId {
|
|
| 207 |
- if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
|
|
| 208 |
- return fmt.Errorf("Error setting devmapper transition ID: %s", err)
|
|
| 209 |
- } |
|
| 210 |
- devices.TransactionId = devices.NewTransactionId |
|
| 211 |
- } |
|
| 212 |
- return nil |
|
| 213 |
-} |
|
| 214 |
- |
|
| 215 |
-func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) {
|
|
| 216 |
- utils.Debugf("registerDevice(%v, %v)", id, hash)
|
|
| 217 |
- info := &DevInfo{
|
|
| 218 |
- Hash: hash, |
|
| 219 |
- DeviceId: id, |
|
| 220 |
- Size: size, |
|
| 221 |
- TransactionId: devices.allocateTransactionId(), |
|
| 222 |
- Initialized: false, |
|
| 223 |
- devices: devices, |
|
| 224 |
- } |
|
| 225 |
- |
|
| 226 |
- devices.Devices[hash] = info |
|
| 227 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 228 |
- // Try to remove unused device |
|
| 229 |
- delete(devices.Devices, hash) |
|
| 230 |
- return nil, err |
|
| 231 |
- } |
|
| 232 |
- |
|
| 233 |
- return info, nil |
|
| 234 |
-} |
|
| 235 |
- |
|
| 236 |
-func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error {
|
|
| 237 |
- utils.Debugf("activateDeviceIfNeeded(%v)", hash)
|
|
| 238 |
- info := devices.Devices[hash] |
|
| 239 |
- if info == nil {
|
|
| 240 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 241 |
- } |
|
| 242 |
- |
|
| 243 |
- if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
|
|
| 244 |
- return nil |
|
| 245 |
- } |
|
| 246 |
- |
|
| 247 |
- return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size) |
|
| 248 |
-} |
|
| 249 |
- |
|
| 250 |
-func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
|
|
| 251 |
- devname := info.DevName() |
|
| 252 |
- |
|
| 253 |
- err := execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname)
|
|
| 254 |
- if err != nil {
|
|
| 255 |
- err = execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname)
|
|
| 256 |
- } |
|
| 257 |
- if err != nil {
|
|
| 258 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 259 |
- return err |
|
| 260 |
- } |
|
| 261 |
- return nil |
|
| 262 |
-} |
|
| 263 |
- |
|
| 264 |
-func (devices *DeviceSet) loadMetaData() error {
|
|
| 265 |
- utils.Debugf("loadMetadata()")
|
|
| 266 |
- defer utils.Debugf("loadMetadata END")
|
|
| 267 |
- _, _, _, params, err := getStatus(devices.getPoolName()) |
|
| 268 |
- if err != nil {
|
|
| 269 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 270 |
- return err |
|
| 271 |
- } |
|
| 272 |
- |
|
| 273 |
- if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
|
|
| 274 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 275 |
- return err |
|
| 276 |
- } |
|
| 277 |
- devices.NewTransactionId = devices.TransactionId |
|
| 278 |
- |
|
| 279 |
- jsonData, err := ioutil.ReadFile(devices.jsonFile()) |
|
| 280 |
- if err != nil && !osIsNotExist(err) {
|
|
| 281 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 282 |
- return err |
|
| 283 |
- } |
|
| 284 |
- |
|
| 285 |
- devices.MetaData.Devices = make(map[string]*DevInfo) |
|
| 286 |
- if jsonData != nil {
|
|
| 287 |
- if err := json.Unmarshal(jsonData, &devices.MetaData); err != nil {
|
|
| 288 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 289 |
- return err |
|
| 290 |
- } |
|
| 291 |
- } |
|
| 292 |
- |
|
| 293 |
- for hash, d := range devices.Devices {
|
|
| 294 |
- d.Hash = hash |
|
| 295 |
- d.devices = devices |
|
| 296 |
- |
|
| 297 |
- if d.DeviceId >= devices.nextFreeDevice {
|
|
| 298 |
- devices.nextFreeDevice = d.DeviceId + 1 |
|
| 299 |
- } |
|
| 300 |
- |
|
| 301 |
- // If the transaction id is larger than the actual one we lost the device due to some crash |
|
| 302 |
- if d.TransactionId > devices.TransactionId {
|
|
| 303 |
- utils.Debugf("Removing lost device %s with id %d", hash, d.TransactionId)
|
|
| 304 |
- delete(devices.Devices, hash) |
|
| 305 |
- } |
|
| 306 |
- } |
|
| 307 |
- return nil |
|
| 308 |
-} |
|
| 309 |
- |
|
| 310 |
-func (devices *DeviceSet) setupBaseImage() error {
|
|
| 311 |
- oldInfo := devices.Devices[""] |
|
| 312 |
- if oldInfo != nil && oldInfo.Initialized {
|
|
| 313 |
- return nil |
|
| 314 |
- } |
|
| 315 |
- |
|
| 316 |
- if oldInfo != nil && !oldInfo.Initialized {
|
|
| 317 |
- utils.Debugf("Removing uninitialized base image")
|
|
| 318 |
- if err := devices.deleteDevice(""); err != nil {
|
|
| 319 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 320 |
- return err |
|
| 321 |
- } |
|
| 322 |
- } |
|
| 323 |
- |
|
| 324 |
- utils.Debugf("Initializing base device-manager snapshot")
|
|
| 325 |
- |
|
| 326 |
- id := devices.allocateDeviceId() |
|
| 327 |
- |
|
| 328 |
- // Create initial device |
|
| 329 |
- if err := createDevice(devices.getPoolDevName(), id); err != nil {
|
|
| 330 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 331 |
- return err |
|
| 332 |
- } |
|
| 333 |
- |
|
| 334 |
- utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
|
|
| 335 |
- info, err := devices.registerDevice(id, "", DefaultBaseFsSize) |
|
| 336 |
- if err != nil {
|
|
| 337 |
- _ = deleteDevice(devices.getPoolDevName(), id) |
|
| 338 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 339 |
- return err |
|
| 340 |
- } |
|
| 341 |
- |
|
| 342 |
- utils.Debugf("Creating filesystem on base device-manager snapshot")
|
|
| 343 |
- |
|
| 344 |
- if err = devices.activateDeviceIfNeeded(""); err != nil {
|
|
| 345 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 346 |
- return err |
|
| 347 |
- } |
|
| 348 |
- |
|
| 349 |
- if err := devices.createFilesystem(info); err != nil {
|
|
| 350 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 351 |
- return err |
|
| 352 |
- } |
|
| 353 |
- |
|
| 354 |
- info.Initialized = true |
|
| 355 |
- if err = devices.saveMetadata(); err != nil {
|
|
| 356 |
- info.Initialized = false |
|
| 357 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 358 |
- return err |
|
| 359 |
- } |
|
| 360 |
- |
|
| 361 |
- return nil |
|
| 362 |
-} |
|
| 363 |
- |
|
| 364 |
-func setCloseOnExec(name string) {
|
|
| 365 |
- if fileInfos, _ := ioutil.ReadDir("/proc/self/fd"); fileInfos != nil {
|
|
| 366 |
- for _, i := range fileInfos {
|
|
| 367 |
- link, _ := osReadlink(filepath.Join("/proc/self/fd", i.Name()))
|
|
| 368 |
- if link == name {
|
|
| 369 |
- fd, err := strconv.Atoi(i.Name()) |
|
| 370 |
- if err == nil {
|
|
| 371 |
- sysCloseOnExec(fd) |
|
| 372 |
- } |
|
| 373 |
- } |
|
| 374 |
- } |
|
| 375 |
- } |
|
| 376 |
-} |
|
| 377 |
- |
|
| 378 |
-func (devices *DeviceSet) log(level int, file string, line int, dmError int, message string) {
|
|
| 379 |
- if level >= 7 {
|
|
| 380 |
- return // Ignore _LOG_DEBUG |
|
| 381 |
- } |
|
| 382 |
- |
|
| 383 |
- if strings.Contains(message, "busy") {
|
|
| 384 |
- devices.sawBusy = true |
|
| 385 |
- } |
|
| 386 |
- |
|
| 387 |
- utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 388 |
-} |
|
| 389 |
- |
|
| 390 |
-func major(device uint64) uint64 {
|
|
| 391 |
- return (device >> 8) & 0xfff |
|
| 392 |
-} |
|
| 393 |
- |
|
| 394 |
-func minor(device uint64) uint64 {
|
|
| 395 |
- return (device & 0xff) | ((device >> 12) & 0xfff00) |
|
| 396 |
-} |
|
| 397 |
- |
|
| 398 |
-func (devices *DeviceSet) ResizePool(size int64) error {
|
|
| 399 |
- dirname := devices.loopbackDir() |
|
| 400 |
- datafilename := path.Join(dirname, "data") |
|
| 401 |
- metadatafilename := path.Join(dirname, "metadata") |
|
| 402 |
- |
|
| 403 |
- datafile, err := osOpenFile(datafilename, osORdWr, 0) |
|
| 404 |
- if datafile == nil {
|
|
| 405 |
- return err |
|
| 406 |
- } |
|
| 407 |
- defer datafile.Close() |
|
| 408 |
- |
|
| 409 |
- fi, err := datafile.Stat() |
|
| 410 |
- if fi == nil {
|
|
| 411 |
- return err |
|
| 412 |
- } |
|
| 413 |
- |
|
| 414 |
- if fi.Size() > size {
|
|
| 415 |
- return fmt.Errorf("Can't shrink file")
|
|
| 416 |
- } |
|
| 417 |
- |
|
| 418 |
- dataloopback := FindLoopDeviceFor(datafile) |
|
| 419 |
- if dataloopback == nil {
|
|
| 420 |
- return fmt.Errorf("Unable to find loopback mount for: %s", datafilename)
|
|
| 421 |
- } |
|
| 422 |
- defer dataloopback.Close() |
|
| 423 |
- |
|
| 424 |
- metadatafile, err := osOpenFile(metadatafilename, osORdWr, 0) |
|
| 425 |
- if metadatafile == nil {
|
|
| 426 |
- return err |
|
| 427 |
- } |
|
| 428 |
- defer metadatafile.Close() |
|
| 429 |
- |
|
| 430 |
- metadataloopback := FindLoopDeviceFor(metadatafile) |
|
| 431 |
- if metadataloopback == nil {
|
|
| 432 |
- return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename)
|
|
| 433 |
- } |
|
| 434 |
- defer metadataloopback.Close() |
|
| 435 |
- |
|
| 436 |
- // Grow loopback file |
|
| 437 |
- if err := datafile.Truncate(size); err != nil {
|
|
| 438 |
- return fmt.Errorf("Unable to grow loopback file: %s", err)
|
|
| 439 |
- } |
|
| 440 |
- |
|
| 441 |
- // Reload size for loopback device |
|
| 442 |
- if err := LoopbackSetCapacity(dataloopback); err != nil {
|
|
| 443 |
- return fmt.Errorf("Unable to update loopback capacity: %s", err)
|
|
| 444 |
- } |
|
| 445 |
- |
|
| 446 |
- // Suspend the pool |
|
| 447 |
- if err := suspendDevice(devices.getPoolName()); err != nil {
|
|
| 448 |
- return fmt.Errorf("Unable to suspend pool: %s", err)
|
|
| 449 |
- } |
|
| 450 |
- |
|
| 451 |
- // Reload with the new block sizes |
|
| 452 |
- if err := reloadPool(devices.getPoolName(), dataloopback, metadataloopback); err != nil {
|
|
| 453 |
- return fmt.Errorf("Unable to reload pool: %s", err)
|
|
| 454 |
- } |
|
| 455 |
- |
|
| 456 |
- // Resume the pool |
|
| 457 |
- if err := resumeDevice(devices.getPoolName()); err != nil {
|
|
| 458 |
- return fmt.Errorf("Unable to resume pool: %s", err)
|
|
| 459 |
- } |
|
| 460 |
- |
|
| 461 |
- return nil |
|
| 462 |
-} |
|
| 463 |
- |
|
| 464 |
-func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|
| 465 |
- logInit(devices) |
|
| 466 |
- |
|
| 467 |
- // Make sure the sparse images exist in <root>/devicemapper/data and |
|
| 468 |
- // <root>/devicemapper/metadata |
|
| 469 |
- |
|
| 470 |
- hasData := devices.hasImage("data")
|
|
| 471 |
- hasMetadata := devices.hasImage("metadata")
|
|
| 472 |
- |
|
| 473 |
- if !doInit && !hasData {
|
|
| 474 |
- return errors.New("Loopback data file not found")
|
|
| 475 |
- } |
|
| 476 |
- |
|
| 477 |
- if !doInit && !hasMetadata {
|
|
| 478 |
- return errors.New("Loopback metadata file not found")
|
|
| 479 |
- } |
|
| 480 |
- |
|
| 481 |
- createdLoopback := !hasData || !hasMetadata |
|
| 482 |
- data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
|
|
| 483 |
- if err != nil {
|
|
| 484 |
- utils.Debugf("Error device ensureImage (data): %s\n", err)
|
|
| 485 |
- return err |
|
| 486 |
- } |
|
| 487 |
- metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
|
|
| 488 |
- if err != nil {
|
|
| 489 |
- utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
|
| 490 |
- return err |
|
| 491 |
- } |
|
| 492 |
- |
|
| 493 |
- // Set the device prefix from the device id and inode of the docker root dir |
|
| 494 |
- |
|
| 495 |
- st, err := osStat(devices.root) |
|
| 496 |
- if err != nil {
|
|
| 497 |
- return fmt.Errorf("Error looking up dir %s: %s", devices.root, err)
|
|
| 498 |
- } |
|
| 499 |
- sysSt := toSysStatT(st.Sys()) |
|
| 500 |
- // "reg-" stands for "regular file". |
|
| 501 |
- // In the future we might use "dev-" for "device file", etc. |
|
| 502 |
- // docker-maj,min[-inode] stands for: |
|
| 503 |
- // - Managed by docker |
|
| 504 |
- // - The target of this device is at major <maj> and minor <min> |
|
| 505 |
- // - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself. |
|
| 506 |
- devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
|
|
| 507 |
- utils.Debugf("Generated prefix: %s", devices.devicePrefix)
|
|
| 508 |
- |
|
| 509 |
- // Check for the existence of the device <prefix>-pool |
|
| 510 |
- utils.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
|
|
| 511 |
- info, err := getInfo(devices.getPoolName()) |
|
| 512 |
- if info == nil {
|
|
| 513 |
- utils.Debugf("Error device getInfo: %s", err)
|
|
| 514 |
- return err |
|
| 515 |
- } |
|
| 516 |
- |
|
| 517 |
- // It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files |
|
| 518 |
- // that are not Close-on-exec, and lxc-start will die if it inherits any unexpected files, |
|
| 519 |
- // so we add this badhack to make sure it closes itself |
|
| 520 |
- setCloseOnExec("/dev/mapper/control")
|
|
| 521 |
- |
|
| 522 |
- // If the pool doesn't exist, create it |
|
| 523 |
- if info.Exists == 0 {
|
|
| 524 |
- utils.Debugf("Pool doesn't exist. Creating it.")
|
|
| 525 |
- |
|
| 526 |
- dataFile, err := attachLoopDevice(data) |
|
| 527 |
- if err != nil {
|
|
| 528 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 529 |
- return err |
|
| 530 |
- } |
|
| 531 |
- defer dataFile.Close() |
|
| 532 |
- |
|
| 533 |
- metadataFile, err := attachLoopDevice(metadata) |
|
| 534 |
- if err != nil {
|
|
| 535 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 536 |
- return err |
|
| 537 |
- } |
|
| 538 |
- defer metadataFile.Close() |
|
| 539 |
- |
|
| 540 |
- if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
|
|
| 541 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 542 |
- return err |
|
| 543 |
- } |
|
| 544 |
- } |
|
| 545 |
- |
|
| 546 |
- // If we didn't just create the data or metadata image, we need to |
|
| 547 |
- // load the metadata from the existing file. |
|
| 548 |
- if !createdLoopback {
|
|
| 549 |
- if err = devices.loadMetaData(); err != nil {
|
|
| 550 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 551 |
- return err |
|
| 552 |
- } |
|
| 553 |
- } |
|
| 554 |
- |
|
| 555 |
- // Setup the base image |
|
| 556 |
- if doInit {
|
|
| 557 |
- if err := devices.setupBaseImage(); err != nil {
|
|
| 558 |
- utils.Debugf("Error device setupBaseImage: %s\n", err)
|
|
| 559 |
- return err |
|
| 560 |
- } |
|
| 561 |
- } |
|
| 562 |
- |
|
| 563 |
- return nil |
|
| 564 |
-} |
|
| 565 |
- |
|
| 566 |
-func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
|
|
| 567 |
- devices.Lock() |
|
| 568 |
- defer devices.Unlock() |
|
| 569 |
- |
|
| 570 |
- if devices.Devices[hash] != nil {
|
|
| 571 |
- return fmt.Errorf("hash %s already exists", hash)
|
|
| 572 |
- } |
|
| 573 |
- |
|
| 574 |
- baseInfo := devices.Devices[baseHash] |
|
| 575 |
- if baseInfo == nil {
|
|
| 576 |
- return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
|
|
| 577 |
- } |
|
| 578 |
- |
|
| 579 |
- baseInfo.lock.Lock() |
|
| 580 |
- defer baseInfo.lock.Unlock() |
|
| 581 |
- |
|
| 582 |
- deviceId := devices.allocateDeviceId() |
|
| 583 |
- |
|
| 584 |
- if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
|
|
| 585 |
- utils.Debugf("Error creating snap device: %s\n", err)
|
|
| 586 |
- return err |
|
| 587 |
- } |
|
| 588 |
- |
|
| 589 |
- if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
|
|
| 590 |
- deleteDevice(devices.getPoolDevName(), deviceId) |
|
| 591 |
- utils.Debugf("Error registering device: %s\n", err)
|
|
| 592 |
- return err |
|
| 593 |
- } |
|
| 594 |
- return nil |
|
| 595 |
-} |
|
| 596 |
- |
|
| 597 |
-func (devices *DeviceSet) deleteDevice(hash string) error {
|
|
| 598 |
- info := devices.Devices[hash] |
|
| 599 |
- if info == nil {
|
|
| 600 |
- return fmt.Errorf("hash %s doesn't exists", hash)
|
|
| 601 |
- } |
|
| 602 |
- |
|
| 603 |
- // This is a workaround for the kernel not discarding block so |
|
| 604 |
- // on the thin pool when we remove a thinp device, so we do it |
|
| 605 |
- // manually |
|
| 606 |
- if err := devices.activateDeviceIfNeeded(hash); err == nil {
|
|
| 607 |
- if err := BlockDeviceDiscard(info.DevName()); err != nil {
|
|
| 608 |
- utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
|
|
| 609 |
- } |
|
| 610 |
- } |
|
| 611 |
- |
|
| 612 |
- devinfo, _ := getInfo(info.Name()) |
|
| 613 |
- if devinfo != nil && devinfo.Exists != 0 {
|
|
| 614 |
- if err := devices.removeDeviceAndWait(info.Name()); err != nil {
|
|
| 615 |
- utils.Debugf("Error removing device: %s\n", err)
|
|
| 616 |
- return err |
|
| 617 |
- } |
|
| 618 |
- } |
|
| 619 |
- |
|
| 620 |
- if info.Initialized {
|
|
| 621 |
- info.Initialized = false |
|
| 622 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 623 |
- utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 624 |
- return err |
|
| 625 |
- } |
|
| 626 |
- } |
|
| 627 |
- |
|
| 628 |
- if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
|
|
| 629 |
- utils.Debugf("Error deleting device: %s\n", err)
|
|
| 630 |
- return err |
|
| 631 |
- } |
|
| 632 |
- |
|
| 633 |
- devices.allocateTransactionId() |
|
| 634 |
- delete(devices.Devices, info.Hash) |
|
| 635 |
- |
|
| 636 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 637 |
- devices.Devices[info.Hash] = info |
|
| 638 |
- utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 639 |
- return err |
|
| 640 |
- } |
|
| 641 |
- |
|
| 642 |
- return nil |
|
| 643 |
-} |
|
| 644 |
- |
|
| 645 |
-func (devices *DeviceSet) DeleteDevice(hash string) error {
|
|
| 646 |
- devices.Lock() |
|
| 647 |
- defer devices.Unlock() |
|
| 648 |
- |
|
| 649 |
- info := devices.Devices[hash] |
|
| 650 |
- if info == nil {
|
|
| 651 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 652 |
- } |
|
| 653 |
- |
|
| 654 |
- info.lock.Lock() |
|
| 655 |
- defer info.lock.Unlock() |
|
| 656 |
- |
|
| 657 |
- return devices.deleteDevice(hash) |
|
| 658 |
-} |
|
| 659 |
- |
|
| 660 |
-func (devices *DeviceSet) deactivatePool() error {
|
|
| 661 |
- utils.Debugf("[devmapper] deactivatePool()")
|
|
| 662 |
- defer utils.Debugf("[devmapper] deactivatePool END")
|
|
| 663 |
- devname := devices.getPoolDevName() |
|
| 664 |
- devinfo, err := getInfo(devname) |
|
| 665 |
- if err != nil {
|
|
| 666 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 667 |
- return err |
|
| 668 |
- } |
|
| 669 |
- if devinfo.Exists != 0 {
|
|
| 670 |
- return removeDevice(devname) |
|
| 671 |
- } |
|
| 672 |
- |
|
| 673 |
- return nil |
|
| 674 |
-} |
|
| 675 |
- |
|
| 676 |
-func (devices *DeviceSet) deactivateDevice(hash string) error {
|
|
| 677 |
- utils.Debugf("[devmapper] deactivateDevice(%s)", hash)
|
|
| 678 |
- defer utils.Debugf("[devmapper] deactivateDevice END")
|
|
| 679 |
- |
|
| 680 |
- info := devices.Devices[hash] |
|
| 681 |
- if info == nil {
|
|
| 682 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 683 |
- } |
|
| 684 |
- devinfo, err := getInfo(info.Name()) |
|
| 685 |
- if err != nil {
|
|
| 686 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 687 |
- return err |
|
| 688 |
- } |
|
| 689 |
- if devinfo.Exists != 0 {
|
|
| 690 |
- if err := devices.removeDeviceAndWait(info.Name()); err != nil {
|
|
| 691 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 692 |
- return err |
|
| 693 |
- } |
|
| 694 |
- } |
|
| 695 |
- |
|
| 696 |
- return nil |
|
| 697 |
-} |
|
| 698 |
- |
|
| 699 |
-// Issues the underlying dm remove operation and then waits |
|
| 700 |
-// for it to finish. |
|
| 701 |
-func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
|
|
| 702 |
- var err error |
|
| 703 |
- |
|
| 704 |
- for i := 0; i < 1000; i++ {
|
|
| 705 |
- devices.sawBusy = false |
|
| 706 |
- err = removeDevice(devname) |
|
| 707 |
- if err == nil {
|
|
| 708 |
- break |
|
| 709 |
- } |
|
| 710 |
- if !devices.sawBusy {
|
|
| 711 |
- return err |
|
| 712 |
- } |
|
| 713 |
- |
|
| 714 |
- // If we see EBUSY it may be a transient error, |
|
| 715 |
- // sleep a bit a retry a few times. |
|
| 716 |
- devices.Unlock() |
|
| 717 |
- time.Sleep(10 * time.Millisecond) |
|
| 718 |
- devices.Lock() |
|
| 719 |
- } |
|
| 720 |
- if err != nil {
|
|
| 721 |
- return err |
|
| 722 |
- } |
|
| 723 |
- |
|
| 724 |
- if err := devices.waitRemove(devname); err != nil {
|
|
| 725 |
- return err |
|
| 726 |
- } |
|
| 727 |
- return nil |
|
| 728 |
-} |
|
| 729 |
- |
|
| 730 |
-// waitRemove blocks until either: |
|
| 731 |
-// a) the device registered at <device_set_prefix>-<hash> is removed, |
|
| 732 |
-// or b) the 1 second timeout expires. |
|
| 733 |
-func (devices *DeviceSet) waitRemove(devname string) error {
|
|
| 734 |
- utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname)
|
|
| 735 |
- defer utils.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname)
|
|
| 736 |
- i := 0 |
|
| 737 |
- for ; i < 1000; i += 1 {
|
|
| 738 |
- devinfo, err := getInfo(devname) |
|
| 739 |
- if err != nil {
|
|
| 740 |
- // If there is an error we assume the device doesn't exist. |
|
| 741 |
- // The error might actually be something else, but we can't differentiate. |
|
| 742 |
- return nil |
|
| 743 |
- } |
|
| 744 |
- if i%100 == 0 {
|
|
| 745 |
- utils.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
|
|
| 746 |
- } |
|
| 747 |
- if devinfo.Exists == 0 {
|
|
| 748 |
- break |
|
| 749 |
- } |
|
| 750 |
- |
|
| 751 |
- devices.Unlock() |
|
| 752 |
- time.Sleep(10 * time.Millisecond) |
|
| 753 |
- devices.Lock() |
|
| 754 |
- } |
|
| 755 |
- if i == 1000 {
|
|
| 756 |
- return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
|
|
| 757 |
- } |
|
| 758 |
- return nil |
|
| 759 |
-} |
|
| 760 |
- |
|
| 761 |
-// waitClose blocks until either: |
|
| 762 |
-// a) the device registered at <device_set_prefix>-<hash> is closed, |
|
| 763 |
-// or b) the 1 second timeout expires. |
|
| 764 |
-func (devices *DeviceSet) waitClose(hash string) error {
|
|
| 765 |
- info := devices.Devices[hash] |
|
| 766 |
- if info == nil {
|
|
| 767 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 768 |
- } |
|
| 769 |
- i := 0 |
|
| 770 |
- for ; i < 1000; i += 1 {
|
|
| 771 |
- devinfo, err := getInfo(info.Name()) |
|
| 772 |
- if err != nil {
|
|
| 773 |
- return err |
|
| 774 |
- } |
|
| 775 |
- if i%100 == 0 {
|
|
| 776 |
- utils.Debugf("Waiting for unmount of %s: opencount=%d", hash, devinfo.OpenCount)
|
|
| 777 |
- } |
|
| 778 |
- if devinfo.OpenCount == 0 {
|
|
| 779 |
- break |
|
| 780 |
- } |
|
| 781 |
- time.Sleep(1 * time.Millisecond) |
|
| 782 |
- } |
|
| 783 |
- if i == 1000 {
|
|
| 784 |
- return fmt.Errorf("Timeout while waiting for device %s to close", hash)
|
|
| 785 |
- } |
|
| 786 |
- return nil |
|
| 787 |
-} |
|
| 788 |
- |
|
| 789 |
-func (devices *DeviceSet) Shutdown() error {
|
|
| 790 |
- devices.Lock() |
|
| 791 |
- defer devices.Unlock() |
|
| 792 |
- |
|
| 793 |
- utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
|
|
| 794 |
- utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
|
|
| 795 |
- defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
|
|
| 796 |
- |
|
| 797 |
- for _, info := range devices.Devices {
|
|
| 798 |
- info.lock.Lock() |
|
| 799 |
- if info.mountCount > 0 {
|
|
| 800 |
- if err := sysUnmount(info.mountPath, 0); err != nil {
|
|
| 801 |
- utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
|
|
| 802 |
- } |
|
| 803 |
- } |
|
| 804 |
- info.lock.Unlock() |
|
| 805 |
- } |
|
| 806 |
- |
|
| 807 |
- for _, d := range devices.Devices {
|
|
| 808 |
- d.lock.Lock() |
|
| 809 |
- |
|
| 810 |
- if err := devices.waitClose(d.Hash); err != nil {
|
|
| 811 |
- utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
|
|
| 812 |
- } |
|
| 813 |
- if err := devices.deactivateDevice(d.Hash); err != nil {
|
|
| 814 |
- utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
|
|
| 815 |
- } |
|
| 816 |
- |
|
| 817 |
- d.lock.Unlock() |
|
| 818 |
- } |
|
| 819 |
- |
|
| 820 |
- if err := devices.deactivatePool(); err != nil {
|
|
| 821 |
- utils.Debugf("Shutdown deactivate pool , error: %s\n", err)
|
|
| 822 |
- } |
|
| 823 |
- |
|
| 824 |
- return nil |
|
| 825 |
-} |
|
| 826 |
- |
|
| 827 |
-func (devices *DeviceSet) MountDevice(hash, path string) error {
|
|
| 828 |
- devices.Lock() |
|
| 829 |
- defer devices.Unlock() |
|
| 830 |
- |
|
| 831 |
- info := devices.Devices[hash] |
|
| 832 |
- if info == nil {
|
|
| 833 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 834 |
- } |
|
| 835 |
- |
|
| 836 |
- info.lock.Lock() |
|
| 837 |
- defer info.lock.Unlock() |
|
| 838 |
- |
|
| 839 |
- if info.mountCount > 0 {
|
|
| 840 |
- if path != info.mountPath {
|
|
| 841 |
- return fmt.Errorf("Trying to mount devmapper device in multple places (%s, %s)", info.mountPath, path)
|
|
| 842 |
- } |
|
| 843 |
- |
|
| 844 |
- if info.floating {
|
|
| 845 |
- // Steal floating ref |
|
| 846 |
- info.floating = false |
|
| 847 |
- } else {
|
|
| 848 |
- info.mountCount++ |
|
| 849 |
- } |
|
| 850 |
- return nil |
|
| 851 |
- } |
|
| 852 |
- |
|
| 853 |
- if err := devices.activateDeviceIfNeeded(hash); err != nil {
|
|
| 854 |
- return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
|
|
| 855 |
- } |
|
| 856 |
- |
|
| 857 |
- var flags uintptr = sysMsMgcVal |
|
| 858 |
- |
|
| 859 |
- err := sysMount(info.DevName(), path, "ext4", flags, "discard") |
|
| 860 |
- if err != nil && err == sysEInval {
|
|
| 861 |
- err = sysMount(info.DevName(), path, "ext4", flags, "") |
|
| 862 |
- } |
|
| 863 |
- if err != nil {
|
|
| 864 |
- return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
|
| 865 |
- } |
|
| 866 |
- |
|
| 867 |
- info.mountCount = 1 |
|
| 868 |
- info.mountPath = path |
|
| 869 |
- info.floating = false |
|
| 870 |
- |
|
| 871 |
- return devices.setInitialized(hash) |
|
| 872 |
-} |
|
| 873 |
- |
|
| 874 |
-func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error {
|
|
| 875 |
- utils.Debugf("[devmapper] UnmountDevice(hash=%s, mode=%d)", hash, mode)
|
|
| 876 |
- defer utils.Debugf("[devmapper] UnmountDevice END")
|
|
| 877 |
- devices.Lock() |
|
| 878 |
- defer devices.Unlock() |
|
| 879 |
- |
|
| 880 |
- info := devices.Devices[hash] |
|
| 881 |
- if info == nil {
|
|
| 882 |
- return fmt.Errorf("UnmountDevice: no such device %s\n", hash)
|
|
| 883 |
- } |
|
| 884 |
- |
|
| 885 |
- info.lock.Lock() |
|
| 886 |
- defer info.lock.Unlock() |
|
| 887 |
- |
|
| 888 |
- if mode == UnmountFloat {
|
|
| 889 |
- if info.floating {
|
|
| 890 |
- return fmt.Errorf("UnmountDevice: can't float floating reference %s\n", hash)
|
|
| 891 |
- } |
|
| 892 |
- |
|
| 893 |
- // Leave this reference floating |
|
| 894 |
- info.floating = true |
|
| 895 |
- return nil |
|
| 896 |
- } |
|
| 897 |
- |
|
| 898 |
- if mode == UnmountSink {
|
|
| 899 |
- if !info.floating {
|
|
| 900 |
- // Someone already sunk this |
|
| 901 |
- return nil |
|
| 902 |
- } |
|
| 903 |
- // Otherwise, treat this as a regular unmount |
|
| 904 |
- } |
|
| 905 |
- |
|
| 906 |
- if info.mountCount == 0 {
|
|
| 907 |
- return fmt.Errorf("UnmountDevice: device not-mounted id %s\n", hash)
|
|
| 908 |
- } |
|
| 909 |
- |
|
| 910 |
- info.mountCount-- |
|
| 911 |
- if info.mountCount > 0 {
|
|
| 912 |
- return nil |
|
| 913 |
- } |
|
| 914 |
- |
|
| 915 |
- utils.Debugf("[devmapper] Unmount(%s)", info.mountPath)
|
|
| 916 |
- if err := sysUnmount(info.mountPath, 0); err != nil {
|
|
| 917 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 918 |
- return err |
|
| 919 |
- } |
|
| 920 |
- utils.Debugf("[devmapper] Unmount done")
|
|
| 921 |
- // Wait for the unmount to be effective, |
|
| 922 |
- // by watching the value of Info.OpenCount for the device |
|
| 923 |
- if err := devices.waitClose(hash); err != nil {
|
|
| 924 |
- return err |
|
| 925 |
- } |
|
| 926 |
- |
|
| 927 |
- devices.deactivateDevice(hash) |
|
| 928 |
- |
|
| 929 |
- info.mountPath = "" |
|
| 930 |
- |
|
| 931 |
- return nil |
|
| 932 |
-} |
|
| 933 |
- |
|
| 934 |
-func (devices *DeviceSet) HasDevice(hash string) bool {
|
|
| 935 |
- devices.Lock() |
|
| 936 |
- defer devices.Unlock() |
|
| 937 |
- |
|
| 938 |
- return devices.Devices[hash] != nil |
|
| 939 |
-} |
|
| 940 |
- |
|
| 941 |
-func (devices *DeviceSet) HasInitializedDevice(hash string) bool {
|
|
| 942 |
- devices.Lock() |
|
| 943 |
- defer devices.Unlock() |
|
| 944 |
- |
|
| 945 |
- info := devices.Devices[hash] |
|
| 946 |
- return info != nil && info.Initialized |
|
| 947 |
-} |
|
| 948 |
- |
|
| 949 |
-func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
|
|
| 950 |
- devices.Lock() |
|
| 951 |
- defer devices.Unlock() |
|
| 952 |
- |
|
| 953 |
- info := devices.Devices[hash] |
|
| 954 |
- if info == nil {
|
|
| 955 |
- return false |
|
| 956 |
- } |
|
| 957 |
- |
|
| 958 |
- info.lock.Lock() |
|
| 959 |
- defer info.lock.Unlock() |
|
| 960 |
- |
|
| 961 |
- devinfo, _ := getInfo(info.Name()) |
|
| 962 |
- return devinfo != nil && devinfo.Exists != 0 |
|
| 963 |
-} |
|
| 964 |
- |
|
| 965 |
-func (devices *DeviceSet) setInitialized(hash string) error {
|
|
| 966 |
- info := devices.Devices[hash] |
|
| 967 |
- if info == nil {
|
|
| 968 |
- return fmt.Errorf("Unknown device %s", hash)
|
|
| 969 |
- } |
|
| 970 |
- |
|
| 971 |
- info.Initialized = true |
|
| 972 |
- if err := devices.saveMetadata(); err != nil {
|
|
| 973 |
- info.Initialized = false |
|
| 974 |
- utils.Debugf("\n--->Err: %s\n", err)
|
|
| 975 |
- return err |
|
| 976 |
- } |
|
| 977 |
- |
|
| 978 |
- return nil |
|
| 979 |
-} |
|
| 980 |
- |
|
| 981 |
-func (devices *DeviceSet) List() []string {
|
|
| 982 |
- devices.Lock() |
|
| 983 |
- defer devices.Unlock() |
|
| 984 |
- |
|
| 985 |
- ids := make([]string, len(devices.Devices)) |
|
| 986 |
- i := 0 |
|
| 987 |
- for k := range devices.Devices {
|
|
| 988 |
- ids[i] = k |
|
| 989 |
- i++ |
|
| 990 |
- } |
|
| 991 |
- return ids |
|
| 992 |
-} |
|
| 993 |
- |
|
| 994 |
-func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) {
|
|
| 995 |
- var params string |
|
| 996 |
- _, sizeInSectors, _, params, err = getStatus(devName) |
|
| 997 |
- if err != nil {
|
|
| 998 |
- return |
|
| 999 |
- } |
|
| 1000 |
- if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err == nil {
|
|
| 1001 |
- return |
|
| 1002 |
- } |
|
| 1003 |
- return |
|
| 1004 |
-} |
|
| 1005 |
- |
|
| 1006 |
-func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) {
|
|
| 1007 |
- devices.Lock() |
|
| 1008 |
- defer devices.Unlock() |
|
| 1009 |
- |
|
| 1010 |
- info := devices.Devices[hash] |
|
| 1011 |
- if info == nil {
|
|
| 1012 |
- return nil, fmt.Errorf("No device %s", hash)
|
|
| 1013 |
- } |
|
| 1014 |
- |
|
| 1015 |
- info.lock.Lock() |
|
| 1016 |
- defer info.lock.Unlock() |
|
| 1017 |
- |
|
| 1018 |
- status := &DevStatus{
|
|
| 1019 |
- DeviceId: info.DeviceId, |
|
| 1020 |
- Size: info.Size, |
|
| 1021 |
- TransactionId: info.TransactionId, |
|
| 1022 |
- } |
|
| 1023 |
- |
|
| 1024 |
- if err := devices.activateDeviceIfNeeded(hash); err != nil {
|
|
| 1025 |
- return nil, fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
|
|
| 1026 |
- } |
|
| 1027 |
- |
|
| 1028 |
- if sizeInSectors, mappedSectors, highestMappedSector, err := devices.deviceStatus(info.DevName()); err != nil {
|
|
| 1029 |
- return nil, err |
|
| 1030 |
- } else {
|
|
| 1031 |
- status.SizeInSectors = sizeInSectors |
|
| 1032 |
- status.MappedSectors = mappedSectors |
|
| 1033 |
- status.HighestMappedSector = highestMappedSector |
|
| 1034 |
- } |
|
| 1035 |
- |
|
| 1036 |
- return status, nil |
|
| 1037 |
-} |
|
| 1038 |
- |
|
| 1039 |
-func (devices *DeviceSet) poolStatus() (totalSizeInSectors, transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64, err error) {
|
|
| 1040 |
- var params string |
|
| 1041 |
- if _, totalSizeInSectors, _, params, err = getStatus(devices.getPoolName()); err == nil {
|
|
| 1042 |
- _, err = fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal) |
|
| 1043 |
- } |
|
| 1044 |
- return |
|
| 1045 |
-} |
|
| 1046 |
- |
|
| 1047 |
-func (devices *DeviceSet) Status() *Status {
|
|
| 1048 |
- devices.Lock() |
|
| 1049 |
- defer devices.Unlock() |
|
| 1050 |
- |
|
| 1051 |
- status := &Status{}
|
|
| 1052 |
- |
|
| 1053 |
- status.PoolName = devices.getPoolName() |
|
| 1054 |
- status.DataLoopback = path.Join(devices.loopbackDir(), "data") |
|
| 1055 |
- status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") |
|
| 1056 |
- |
|
| 1057 |
- totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() |
|
| 1058 |
- if err == nil {
|
|
| 1059 |
- // Convert from blocks to bytes |
|
| 1060 |
- blockSizeInSectors := totalSizeInSectors / dataTotal |
|
| 1061 |
- |
|
| 1062 |
- status.Data.Used = dataUsed * blockSizeInSectors * 512 |
|
| 1063 |
- status.Data.Total = dataTotal * blockSizeInSectors * 512 |
|
| 1064 |
- |
|
| 1065 |
- // metadata blocks are always 4k |
|
| 1066 |
- status.Metadata.Used = metadataUsed * 4096 |
|
| 1067 |
- status.Metadata.Total = metadataTotal * 4096 |
|
| 1068 |
- |
|
| 1069 |
- status.SectorSize = blockSizeInSectors * 512 |
|
| 1070 |
- } |
|
| 1071 |
- |
|
| 1072 |
- return status |
|
| 1073 |
-} |
|
| 1074 |
- |
|
| 1075 |
-func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) {
|
|
| 1076 |
- SetDevDir("/dev")
|
|
| 1077 |
- |
|
| 1078 |
- devices := &DeviceSet{
|
|
| 1079 |
- root: root, |
|
| 1080 |
- MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
|
| 1081 |
- } |
|
| 1082 |
- |
|
| 1083 |
- if err := devices.initDevmapper(doInit); err != nil {
|
|
| 1084 |
- return nil, err |
|
| 1085 |
- } |
|
| 1086 |
- |
|
| 1087 |
- return devices, nil |
|
| 1088 |
-} |
| 1089 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,595 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "errors" |
|
| 7 |
- "fmt" |
|
| 8 |
- "github.com/dotcloud/docker/utils" |
|
| 9 |
- "runtime" |
|
| 10 |
- "syscall" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-type DevmapperLogger interface {
|
|
| 14 |
- log(level int, file string, line int, dmError int, message string) |
|
| 15 |
-} |
|
| 16 |
- |
|
| 17 |
-const ( |
|
| 18 |
- DeviceCreate TaskType = iota |
|
| 19 |
- DeviceReload |
|
| 20 |
- DeviceRemove |
|
| 21 |
- DeviceRemoveAll |
|
| 22 |
- DeviceSuspend |
|
| 23 |
- DeviceResume |
|
| 24 |
- DeviceInfo |
|
| 25 |
- DeviceDeps |
|
| 26 |
- DeviceRename |
|
| 27 |
- DeviceVersion |
|
| 28 |
- DeviceStatus |
|
| 29 |
- DeviceTable |
|
| 30 |
- DeviceWaitevent |
|
| 31 |
- DeviceList |
|
| 32 |
- DeviceClear |
|
| 33 |
- DeviceMknodes |
|
| 34 |
- DeviceListVersions |
|
| 35 |
- DeviceTargetMsg |
|
| 36 |
- DeviceSetGeometry |
|
| 37 |
-) |
|
| 38 |
- |
|
| 39 |
-const ( |
|
| 40 |
- AddNodeOnResume AddNodeType = iota |
|
| 41 |
- AddNodeOnCreate |
|
| 42 |
-) |
|
| 43 |
- |
|
| 44 |
-var ( |
|
| 45 |
- ErrTaskRun = errors.New("dm_task_run failed")
|
|
| 46 |
- ErrTaskSetName = errors.New("dm_task_set_name failed")
|
|
| 47 |
- ErrTaskSetMessage = errors.New("dm_task_set_message failed")
|
|
| 48 |
- ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed")
|
|
| 49 |
- ErrTaskSetRo = errors.New("dm_task_set_ro failed")
|
|
| 50 |
- ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
|
| 51 |
- ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
|
| 52 |
- ErrTaskGetInfo = errors.New("dm_task_get_info failed")
|
|
| 53 |
- ErrTaskSetCookie = errors.New("dm_task_set_cookie failed")
|
|
| 54 |
- ErrNilCookie = errors.New("cookie ptr can't be nil")
|
|
| 55 |
- ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
|
|
| 56 |
- ErrGetBlockSize = errors.New("Can't get block size")
|
|
| 57 |
- ErrUdevWait = errors.New("wait on udev cookie failed")
|
|
| 58 |
- ErrSetDevDir = errors.New("dm_set_dev_dir failed")
|
|
| 59 |
- ErrGetLibraryVersion = errors.New("dm_get_library_version failed")
|
|
| 60 |
- ErrCreateRemoveTask = errors.New("Can't create task of type DeviceRemove")
|
|
| 61 |
- ErrRunRemoveDevice = errors.New("running removeDevice failed")
|
|
| 62 |
- ErrInvalidAddNode = errors.New("Invalide AddNoce type")
|
|
| 63 |
- ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
|
|
| 64 |
- ErrLoopbackSetCapacity = errors.New("Unable set loopback capacity")
|
|
| 65 |
-) |
|
| 66 |
- |
|
| 67 |
-type ( |
|
| 68 |
- Task struct {
|
|
| 69 |
- unmanaged *CDmTask |
|
| 70 |
- } |
|
| 71 |
- Info struct {
|
|
| 72 |
- Exists int |
|
| 73 |
- Suspended int |
|
| 74 |
- LiveTable int |
|
| 75 |
- InactiveTable int |
|
| 76 |
- OpenCount int32 |
|
| 77 |
- EventNr uint32 |
|
| 78 |
- Major uint32 |
|
| 79 |
- Minor uint32 |
|
| 80 |
- ReadOnly int |
|
| 81 |
- TargetCount int32 |
|
| 82 |
- } |
|
| 83 |
- TaskType int |
|
| 84 |
- AddNodeType int |
|
| 85 |
-) |
|
| 86 |
- |
|
| 87 |
-func (t *Task) destroy() {
|
|
| 88 |
- if t != nil {
|
|
| 89 |
- DmTaskDestroy(t.unmanaged) |
|
| 90 |
- runtime.SetFinalizer(t, nil) |
|
| 91 |
- } |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-func TaskCreate(tasktype TaskType) *Task {
|
|
| 95 |
- Ctask := DmTaskCreate(int(tasktype)) |
|
| 96 |
- if Ctask == nil {
|
|
| 97 |
- return nil |
|
| 98 |
- } |
|
| 99 |
- task := &Task{unmanaged: Ctask}
|
|
| 100 |
- runtime.SetFinalizer(task, (*Task).destroy) |
|
| 101 |
- return task |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-func (t *Task) Run() error {
|
|
| 105 |
- if res := DmTaskRun(t.unmanaged); res != 1 {
|
|
| 106 |
- return ErrTaskRun |
|
| 107 |
- } |
|
| 108 |
- return nil |
|
| 109 |
-} |
|
| 110 |
- |
|
| 111 |
-func (t *Task) SetName(name string) error {
|
|
| 112 |
- if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
|
| 113 |
- return ErrTaskSetName |
|
| 114 |
- } |
|
| 115 |
- return nil |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-func (t *Task) SetMessage(message string) error {
|
|
| 119 |
- if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
|
| 120 |
- return ErrTaskSetMessage |
|
| 121 |
- } |
|
| 122 |
- return nil |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-func (t *Task) SetSector(sector uint64) error {
|
|
| 126 |
- if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
|
| 127 |
- return ErrTaskSetSector |
|
| 128 |
- } |
|
| 129 |
- return nil |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func (t *Task) SetCookie(cookie *uint, flags uint16) error {
|
|
| 133 |
- if cookie == nil {
|
|
| 134 |
- return ErrNilCookie |
|
| 135 |
- } |
|
| 136 |
- if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
|
|
| 137 |
- return ErrTaskSetCookie |
|
| 138 |
- } |
|
| 139 |
- return nil |
|
| 140 |
-} |
|
| 141 |
- |
|
| 142 |
-func (t *Task) SetAddNode(addNode AddNodeType) error {
|
|
| 143 |
- if addNode != AddNodeOnResume && addNode != AddNodeOnCreate {
|
|
| 144 |
- return ErrInvalidAddNode |
|
| 145 |
- } |
|
| 146 |
- if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
|
| 147 |
- return ErrTaskSetAddNode |
|
| 148 |
- } |
|
| 149 |
- return nil |
|
| 150 |
-} |
|
| 151 |
- |
|
| 152 |
-func (t *Task) SetRo() error {
|
|
| 153 |
- if res := DmTaskSetRo(t.unmanaged); res != 1 {
|
|
| 154 |
- return ErrTaskSetRo |
|
| 155 |
- } |
|
| 156 |
- return nil |
|
| 157 |
-} |
|
| 158 |
- |
|
| 159 |
-func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
|
|
| 160 |
- if res := DmTaskAddTarget(t.unmanaged, start, size, |
|
| 161 |
- ttype, params); res != 1 {
|
|
| 162 |
- return ErrTaskAddTarget |
|
| 163 |
- } |
|
| 164 |
- return nil |
|
| 165 |
-} |
|
| 166 |
- |
|
| 167 |
-func (t *Task) GetInfo() (*Info, error) {
|
|
| 168 |
- info := &Info{}
|
|
| 169 |
- if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
|
| 170 |
- return nil, ErrTaskGetInfo |
|
| 171 |
- } |
|
| 172 |
- return info, nil |
|
| 173 |
-} |
|
| 174 |
- |
|
| 175 |
-func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, |
|
| 176 |
- length uint64, targetType string, params string) {
|
|
| 177 |
- |
|
| 178 |
- return DmGetNextTarget(t.unmanaged, next, &start, &length, |
|
| 179 |
- &targetType, ¶ms), |
|
| 180 |
- start, length, targetType, params |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) {
|
|
| 184 |
- loopInfo, err := ioctlLoopGetStatus64(file.Fd()) |
|
| 185 |
- if err != nil {
|
|
| 186 |
- utils.Errorf("Error get loopback backing file: %s\n", err)
|
|
| 187 |
- return 0, 0, ErrGetLoopbackBackingFile |
|
| 188 |
- } |
|
| 189 |
- return loopInfo.loDevice, loopInfo.loInode, nil |
|
| 190 |
-} |
|
| 191 |
- |
|
| 192 |
-func LoopbackSetCapacity(file *osFile) error {
|
|
| 193 |
- if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
|
|
| 194 |
- utils.Errorf("Error loopbackSetCapacity: %s", err)
|
|
| 195 |
- return ErrLoopbackSetCapacity |
|
| 196 |
- } |
|
| 197 |
- return nil |
|
| 198 |
-} |
|
| 199 |
- |
|
| 200 |
-func FindLoopDeviceFor(file *osFile) *osFile {
|
|
| 201 |
- stat, err := file.Stat() |
|
| 202 |
- if err != nil {
|
|
| 203 |
- return nil |
|
| 204 |
- } |
|
| 205 |
- targetInode := stat.Sys().(*sysStatT).Ino |
|
| 206 |
- targetDevice := stat.Sys().(*sysStatT).Dev |
|
| 207 |
- |
|
| 208 |
- for i := 0; true; i++ {
|
|
| 209 |
- path := fmt.Sprintf("/dev/loop%d", i)
|
|
| 210 |
- |
|
| 211 |
- file, err := osOpenFile(path, osORdWr, 0) |
|
| 212 |
- if err != nil {
|
|
| 213 |
- if osIsNotExist(err) {
|
|
| 214 |
- return nil |
|
| 215 |
- } |
|
| 216 |
- |
|
| 217 |
- // Ignore all errors until the first not-exist |
|
| 218 |
- // we want to continue looking for the file |
|
| 219 |
- continue |
|
| 220 |
- } |
|
| 221 |
- |
|
| 222 |
- dev, inode, err := getLoopbackBackingFile(file) |
|
| 223 |
- if err == nil && dev == targetDevice && inode == targetInode {
|
|
| 224 |
- return file |
|
| 225 |
- } |
|
| 226 |
- file.Close() |
|
| 227 |
- } |
|
| 228 |
- |
|
| 229 |
- return nil |
|
| 230 |
-} |
|
| 231 |
- |
|
| 232 |
-func UdevWait(cookie uint) error {
|
|
| 233 |
- if res := DmUdevWait(cookie); res != 1 {
|
|
| 234 |
- utils.Debugf("Failed to wait on udev cookie %d", cookie)
|
|
| 235 |
- return ErrUdevWait |
|
| 236 |
- } |
|
| 237 |
- return nil |
|
| 238 |
-} |
|
| 239 |
- |
|
| 240 |
-func LogInitVerbose(level int) {
|
|
| 241 |
- DmLogInitVerbose(level) |
|
| 242 |
-} |
|
| 243 |
- |
|
| 244 |
-var dmLogger DevmapperLogger = nil |
|
| 245 |
- |
|
| 246 |
-func logInit(logger DevmapperLogger) {
|
|
| 247 |
- dmLogger = logger |
|
| 248 |
- LogWithErrnoInit() |
|
| 249 |
-} |
|
| 250 |
- |
|
| 251 |
-func SetDevDir(dir string) error {
|
|
| 252 |
- if res := DmSetDevDir(dir); res != 1 {
|
|
| 253 |
- utils.Debugf("Error dm_set_dev_dir")
|
|
| 254 |
- return ErrSetDevDir |
|
| 255 |
- } |
|
| 256 |
- return nil |
|
| 257 |
-} |
|
| 258 |
- |
|
| 259 |
-func GetLibraryVersion() (string, error) {
|
|
| 260 |
- var version string |
|
| 261 |
- if res := DmGetLibraryVersion(&version); res != 1 {
|
|
| 262 |
- return "", ErrGetLibraryVersion |
|
| 263 |
- } |
|
| 264 |
- return version, nil |
|
| 265 |
-} |
|
| 266 |
- |
|
| 267 |
-// Useful helper for cleanup |
|
| 268 |
-func RemoveDevice(name string) error {
|
|
| 269 |
- task := TaskCreate(DeviceRemove) |
|
| 270 |
- if task == nil {
|
|
| 271 |
- return ErrCreateRemoveTask |
|
| 272 |
- } |
|
| 273 |
- if err := task.SetName(name); err != nil {
|
|
| 274 |
- utils.Debugf("Can't set task name %s", name)
|
|
| 275 |
- return err |
|
| 276 |
- } |
|
| 277 |
- if err := task.Run(); err != nil {
|
|
| 278 |
- return ErrRunRemoveDevice |
|
| 279 |
- } |
|
| 280 |
- return nil |
|
| 281 |
-} |
|
| 282 |
- |
|
| 283 |
-func GetBlockDeviceSize(file *osFile) (uint64, error) {
|
|
| 284 |
- size, err := ioctlBlkGetSize64(file.Fd()) |
|
| 285 |
- if err != nil {
|
|
| 286 |
- utils.Errorf("Error getblockdevicesize: %s", err)
|
|
| 287 |
- return 0, ErrGetBlockSize |
|
| 288 |
- } |
|
| 289 |
- return uint64(size), nil |
|
| 290 |
-} |
|
| 291 |
- |
|
| 292 |
-func BlockDeviceDiscard(path string) error {
|
|
| 293 |
- file, err := osOpenFile(path, osORdWr, 0) |
|
| 294 |
- if err != nil {
|
|
| 295 |
- return err |
|
| 296 |
- } |
|
| 297 |
- defer file.Close() |
|
| 298 |
- |
|
| 299 |
- size, err := GetBlockDeviceSize(file) |
|
| 300 |
- if err != nil {
|
|
| 301 |
- return err |
|
| 302 |
- } |
|
| 303 |
- |
|
| 304 |
- if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
|
|
| 305 |
- return err |
|
| 306 |
- } |
|
| 307 |
- |
|
| 308 |
- // Without this sometimes the remove of the device that happens after |
|
| 309 |
- // discard fails with EBUSY. |
|
| 310 |
- syscall.Sync() |
|
| 311 |
- |
|
| 312 |
- return nil |
|
| 313 |
-} |
|
| 314 |
- |
|
| 315 |
-// This is the programmatic example of "dmsetup create" |
|
| 316 |
-func createPool(poolName string, dataFile, metadataFile *osFile) error {
|
|
| 317 |
- task, err := createTask(DeviceCreate, poolName) |
|
| 318 |
- if task == nil {
|
|
| 319 |
- return err |
|
| 320 |
- } |
|
| 321 |
- |
|
| 322 |
- size, err := GetBlockDeviceSize(dataFile) |
|
| 323 |
- if err != nil {
|
|
| 324 |
- return fmt.Errorf("Can't get data size")
|
|
| 325 |
- } |
|
| 326 |
- |
|
| 327 |
- params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768 1 skip_block_zeroing" |
|
| 328 |
- if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 329 |
- return fmt.Errorf("Can't add target")
|
|
| 330 |
- } |
|
| 331 |
- |
|
| 332 |
- var cookie uint = 0 |
|
| 333 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 334 |
- return fmt.Errorf("Can't set cookie")
|
|
| 335 |
- } |
|
| 336 |
- |
|
| 337 |
- if err := task.Run(); err != nil {
|
|
| 338 |
- return fmt.Errorf("Error running DeviceCreate (createPool)")
|
|
| 339 |
- } |
|
| 340 |
- |
|
| 341 |
- UdevWait(cookie) |
|
| 342 |
- |
|
| 343 |
- return nil |
|
| 344 |
-} |
|
| 345 |
- |
|
| 346 |
-func reloadPool(poolName string, dataFile, metadataFile *osFile) error {
|
|
| 347 |
- task, err := createTask(DeviceReload, poolName) |
|
| 348 |
- if task == nil {
|
|
| 349 |
- return err |
|
| 350 |
- } |
|
| 351 |
- |
|
| 352 |
- size, err := GetBlockDeviceSize(dataFile) |
|
| 353 |
- if err != nil {
|
|
| 354 |
- return fmt.Errorf("Can't get data size")
|
|
| 355 |
- } |
|
| 356 |
- |
|
| 357 |
- params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768" |
|
| 358 |
- if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 359 |
- return fmt.Errorf("Can't add target")
|
|
| 360 |
- } |
|
| 361 |
- |
|
| 362 |
- if err := task.Run(); err != nil {
|
|
| 363 |
- return fmt.Errorf("Error running DeviceCreate")
|
|
| 364 |
- } |
|
| 365 |
- |
|
| 366 |
- return nil |
|
| 367 |
-} |
|
| 368 |
- |
|
| 369 |
-func createTask(t TaskType, name string) (*Task, error) {
|
|
| 370 |
- task := TaskCreate(t) |
|
| 371 |
- if task == nil {
|
|
| 372 |
- return nil, fmt.Errorf("Can't create task of type %d", int(t))
|
|
| 373 |
- } |
|
| 374 |
- if err := task.SetName(name); err != nil {
|
|
| 375 |
- return nil, fmt.Errorf("Can't set task name %s", name)
|
|
| 376 |
- } |
|
| 377 |
- return task, nil |
|
| 378 |
-} |
|
| 379 |
- |
|
| 380 |
-func getInfo(name string) (*Info, error) {
|
|
| 381 |
- task, err := createTask(DeviceInfo, name) |
|
| 382 |
- if task == nil {
|
|
| 383 |
- return nil, err |
|
| 384 |
- } |
|
| 385 |
- if err := task.Run(); err != nil {
|
|
| 386 |
- return nil, err |
|
| 387 |
- } |
|
| 388 |
- return task.GetInfo() |
|
| 389 |
-} |
|
| 390 |
- |
|
| 391 |
-func getStatus(name string) (uint64, uint64, string, string, error) {
|
|
| 392 |
- task, err := createTask(DeviceStatus, name) |
|
| 393 |
- if task == nil {
|
|
| 394 |
- utils.Debugf("getStatus: Error createTask: %s", err)
|
|
| 395 |
- return 0, 0, "", "", err |
|
| 396 |
- } |
|
| 397 |
- if err := task.Run(); err != nil {
|
|
| 398 |
- utils.Debugf("getStatus: Error Run: %s", err)
|
|
| 399 |
- return 0, 0, "", "", err |
|
| 400 |
- } |
|
| 401 |
- |
|
| 402 |
- devinfo, err := task.GetInfo() |
|
| 403 |
- if err != nil {
|
|
| 404 |
- utils.Debugf("getStatus: Error GetInfo: %s", err)
|
|
| 405 |
- return 0, 0, "", "", err |
|
| 406 |
- } |
|
| 407 |
- if devinfo.Exists == 0 {
|
|
| 408 |
- utils.Debugf("getStatus: Non existing device %s", name)
|
|
| 409 |
- return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
|
|
| 410 |
- } |
|
| 411 |
- |
|
| 412 |
- _, start, length, targetType, params := task.GetNextTarget(0) |
|
| 413 |
- return start, length, targetType, params, nil |
|
| 414 |
-} |
|
| 415 |
- |
|
| 416 |
-func setTransactionId(poolName string, oldId uint64, newId uint64) error {
|
|
| 417 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 418 |
- if task == nil {
|
|
| 419 |
- return err |
|
| 420 |
- } |
|
| 421 |
- |
|
| 422 |
- if err := task.SetSector(0); err != nil {
|
|
| 423 |
- return fmt.Errorf("Can't set sector")
|
|
| 424 |
- } |
|
| 425 |
- |
|
| 426 |
- if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
|
|
| 427 |
- return fmt.Errorf("Can't set message")
|
|
| 428 |
- } |
|
| 429 |
- |
|
| 430 |
- if err := task.Run(); err != nil {
|
|
| 431 |
- return fmt.Errorf("Error running setTransactionId")
|
|
| 432 |
- } |
|
| 433 |
- return nil |
|
| 434 |
-} |
|
| 435 |
- |
|
| 436 |
-func suspendDevice(name string) error {
|
|
| 437 |
- task, err := createTask(DeviceSuspend, name) |
|
| 438 |
- if task == nil {
|
|
| 439 |
- return err |
|
| 440 |
- } |
|
| 441 |
- if err := task.Run(); err != nil {
|
|
| 442 |
- return fmt.Errorf("Error running DeviceSuspend: %s", err)
|
|
| 443 |
- } |
|
| 444 |
- return nil |
|
| 445 |
-} |
|
| 446 |
- |
|
| 447 |
-func resumeDevice(name string) error {
|
|
| 448 |
- task, err := createTask(DeviceResume, name) |
|
| 449 |
- if task == nil {
|
|
| 450 |
- return err |
|
| 451 |
- } |
|
| 452 |
- |
|
| 453 |
- var cookie uint = 0 |
|
| 454 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 455 |
- return fmt.Errorf("Can't set cookie")
|
|
| 456 |
- } |
|
| 457 |
- |
|
| 458 |
- if err := task.Run(); err != nil {
|
|
| 459 |
- return fmt.Errorf("Error running DeviceResume")
|
|
| 460 |
- } |
|
| 461 |
- |
|
| 462 |
- UdevWait(cookie) |
|
| 463 |
- |
|
| 464 |
- return nil |
|
| 465 |
-} |
|
| 466 |
- |
|
| 467 |
-func createDevice(poolName string, deviceId int) error {
|
|
| 468 |
- utils.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
|
|
| 469 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 470 |
- if task == nil {
|
|
| 471 |
- return err |
|
| 472 |
- } |
|
| 473 |
- |
|
| 474 |
- if err := task.SetSector(0); err != nil {
|
|
| 475 |
- return fmt.Errorf("Can't set sector")
|
|
| 476 |
- } |
|
| 477 |
- |
|
| 478 |
- if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
|
|
| 479 |
- return fmt.Errorf("Can't set message")
|
|
| 480 |
- } |
|
| 481 |
- |
|
| 482 |
- if err := task.Run(); err != nil {
|
|
| 483 |
- return fmt.Errorf("Error running createDevice")
|
|
| 484 |
- } |
|
| 485 |
- return nil |
|
| 486 |
-} |
|
| 487 |
- |
|
| 488 |
-func deleteDevice(poolName string, deviceId int) error {
|
|
| 489 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 490 |
- if task == nil {
|
|
| 491 |
- return err |
|
| 492 |
- } |
|
| 493 |
- |
|
| 494 |
- if err := task.SetSector(0); err != nil {
|
|
| 495 |
- return fmt.Errorf("Can't set sector")
|
|
| 496 |
- } |
|
| 497 |
- |
|
| 498 |
- if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
|
|
| 499 |
- return fmt.Errorf("Can't set message")
|
|
| 500 |
- } |
|
| 501 |
- |
|
| 502 |
- if err := task.Run(); err != nil {
|
|
| 503 |
- return fmt.Errorf("Error running deleteDevice")
|
|
| 504 |
- } |
|
| 505 |
- return nil |
|
| 506 |
-} |
|
| 507 |
- |
|
| 508 |
-func removeDevice(name string) error {
|
|
| 509 |
- utils.Debugf("[devmapper] removeDevice START")
|
|
| 510 |
- defer utils.Debugf("[devmapper] removeDevice END")
|
|
| 511 |
- task, err := createTask(DeviceRemove, name) |
|
| 512 |
- if task == nil {
|
|
| 513 |
- return err |
|
| 514 |
- } |
|
| 515 |
- if err = task.Run(); err != nil {
|
|
| 516 |
- return fmt.Errorf("Error running removeDevice")
|
|
| 517 |
- } |
|
| 518 |
- return nil |
|
| 519 |
-} |
|
| 520 |
- |
|
| 521 |
-func activateDevice(poolName string, name string, deviceId int, size uint64) error {
|
|
| 522 |
- task, err := createTask(DeviceCreate, name) |
|
| 523 |
- if task == nil {
|
|
| 524 |
- return err |
|
| 525 |
- } |
|
| 526 |
- |
|
| 527 |
- params := fmt.Sprintf("%s %d", poolName, deviceId)
|
|
| 528 |
- if err := task.AddTarget(0, size/512, "thin", params); err != nil {
|
|
| 529 |
- return fmt.Errorf("Can't add target")
|
|
| 530 |
- } |
|
| 531 |
- if err := task.SetAddNode(AddNodeOnCreate); err != nil {
|
|
| 532 |
- return fmt.Errorf("Can't add node")
|
|
| 533 |
- } |
|
| 534 |
- |
|
| 535 |
- var cookie uint = 0 |
|
| 536 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 537 |
- return fmt.Errorf("Can't set cookie")
|
|
| 538 |
- } |
|
| 539 |
- |
|
| 540 |
- if err := task.Run(); err != nil {
|
|
| 541 |
- return fmt.Errorf("Error running DeviceCreate (activateDevice)")
|
|
| 542 |
- } |
|
| 543 |
- |
|
| 544 |
- UdevWait(cookie) |
|
| 545 |
- |
|
| 546 |
- return nil |
|
| 547 |
-} |
|
| 548 |
- |
|
| 549 |
-func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
|
|
| 550 |
- devinfo, _ := getInfo(baseName) |
|
| 551 |
- doSuspend := devinfo != nil && devinfo.Exists != 0 |
|
| 552 |
- |
|
| 553 |
- if doSuspend {
|
|
| 554 |
- if err := suspendDevice(baseName); err != nil {
|
|
| 555 |
- return err |
|
| 556 |
- } |
|
| 557 |
- } |
|
| 558 |
- |
|
| 559 |
- task, err := createTask(DeviceTargetMsg, poolName) |
|
| 560 |
- if task == nil {
|
|
| 561 |
- if doSuspend {
|
|
| 562 |
- resumeDevice(baseName) |
|
| 563 |
- } |
|
| 564 |
- return err |
|
| 565 |
- } |
|
| 566 |
- |
|
| 567 |
- if err := task.SetSector(0); err != nil {
|
|
| 568 |
- if doSuspend {
|
|
| 569 |
- resumeDevice(baseName) |
|
| 570 |
- } |
|
| 571 |
- return fmt.Errorf("Can't set sector")
|
|
| 572 |
- } |
|
| 573 |
- |
|
| 574 |
- if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
|
|
| 575 |
- if doSuspend {
|
|
| 576 |
- resumeDevice(baseName) |
|
| 577 |
- } |
|
| 578 |
- return fmt.Errorf("Can't set message")
|
|
| 579 |
- } |
|
| 580 |
- |
|
| 581 |
- if err := task.Run(); err != nil {
|
|
| 582 |
- if doSuspend {
|
|
| 583 |
- resumeDevice(baseName) |
|
| 584 |
- } |
|
| 585 |
- return fmt.Errorf("Error running DeviceCreate (createSnapDevice)")
|
|
| 586 |
- } |
|
| 587 |
- |
|
| 588 |
- if doSuspend {
|
|
| 589 |
- if err := resumeDevice(baseName); err != nil {
|
|
| 590 |
- return err |
|
| 591 |
- } |
|
| 592 |
- } |
|
| 593 |
- |
|
| 594 |
- return nil |
|
| 595 |
-} |
| 596 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,106 +0,0 @@ |
| 1 |
-package devmapper |
|
| 2 |
- |
|
| 3 |
-// Definition of struct dm_task and sub structures (from lvm2) |
|
| 4 |
-// |
|
| 5 |
-// struct dm_ioctl {
|
|
| 6 |
-// /* |
|
| 7 |
-// * The version number is made up of three parts: |
|
| 8 |
-// * major - no backward or forward compatibility, |
|
| 9 |
-// * minor - only backwards compatible, |
|
| 10 |
-// * patch - both backwards and forwards compatible. |
|
| 11 |
-// * |
|
| 12 |
-// * All clients of the ioctl interface should fill in the |
|
| 13 |
-// * version number of the interface that they were |
|
| 14 |
-// * compiled with. |
|
| 15 |
-// * |
|
| 16 |
-// * All recognised ioctl commands (ie. those that don't |
|
| 17 |
-// * return -ENOTTY) fill out this field, even if the |
|
| 18 |
-// * command failed. |
|
| 19 |
-// */ |
|
| 20 |
-// uint32_t version[3]; /* in/out */ |
|
| 21 |
-// uint32_t data_size; /* total size of data passed in |
|
| 22 |
-// * including this struct */ |
|
| 23 |
- |
|
| 24 |
-// uint32_t data_start; /* offset to start of data |
|
| 25 |
-// * relative to start of this struct */ |
|
| 26 |
- |
|
| 27 |
-// uint32_t target_count; /* in/out */ |
|
| 28 |
-// int32_t open_count; /* out */ |
|
| 29 |
-// uint32_t flags; /* in/out */ |
|
| 30 |
- |
|
| 31 |
-// /* |
|
| 32 |
-// * event_nr holds either the event number (input and output) or the |
|
| 33 |
-// * udev cookie value (input only). |
|
| 34 |
-// * The DM_DEV_WAIT ioctl takes an event number as input. |
|
| 35 |
-// * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls |
|
| 36 |
-// * use the field as a cookie to return in the DM_COOKIE |
|
| 37 |
-// * variable with the uevents they issue. |
|
| 38 |
-// * For output, the ioctls return the event number, not the cookie. |
|
| 39 |
-// */ |
|
| 40 |
-// uint32_t event_nr; /* in/out */ |
|
| 41 |
-// uint32_t padding; |
|
| 42 |
- |
|
| 43 |
-// uint64_t dev; /* in/out */ |
|
| 44 |
- |
|
| 45 |
-// char name[DM_NAME_LEN]; /* device name */ |
|
| 46 |
-// char uuid[DM_UUID_LEN]; /* unique identifier for |
|
| 47 |
-// * the block device */ |
|
| 48 |
-// char data[7]; /* padding or data */ |
|
| 49 |
-// }; |
|
| 50 |
- |
|
| 51 |
-// struct target {
|
|
| 52 |
-// uint64_t start; |
|
| 53 |
-// uint64_t length; |
|
| 54 |
-// char *type; |
|
| 55 |
-// char *params; |
|
| 56 |
- |
|
| 57 |
-// struct target *next; |
|
| 58 |
-// }; |
|
| 59 |
- |
|
| 60 |
-// typedef enum {
|
|
| 61 |
-// DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */ |
|
| 62 |
-// DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */ |
|
| 63 |
-// } dm_add_node_t; |
|
| 64 |
- |
|
| 65 |
-// struct dm_task {
|
|
| 66 |
-// int type; |
|
| 67 |
-// char *dev_name; |
|
| 68 |
-// char *mangled_dev_name; |
|
| 69 |
- |
|
| 70 |
-// struct target *head, *tail; |
|
| 71 |
- |
|
| 72 |
-// int read_only; |
|
| 73 |
-// uint32_t event_nr; |
|
| 74 |
-// int major; |
|
| 75 |
-// int minor; |
|
| 76 |
-// int allow_default_major_fallback; |
|
| 77 |
-// uid_t uid; |
|
| 78 |
-// gid_t gid; |
|
| 79 |
-// mode_t mode; |
|
| 80 |
-// uint32_t read_ahead; |
|
| 81 |
-// uint32_t read_ahead_flags; |
|
| 82 |
-// union {
|
|
| 83 |
-// struct dm_ioctl *v4; |
|
| 84 |
-// } dmi; |
|
| 85 |
-// char *newname; |
|
| 86 |
-// char *message; |
|
| 87 |
-// char *geometry; |
|
| 88 |
-// uint64_t sector; |
|
| 89 |
-// int no_flush; |
|
| 90 |
-// int no_open_count; |
|
| 91 |
-// int skip_lockfs; |
|
| 92 |
-// int query_inactive_table; |
|
| 93 |
-// int suppress_identical_reload; |
|
| 94 |
-// dm_add_node_t add_node; |
|
| 95 |
-// uint64_t existing_table_size; |
|
| 96 |
-// int cookie_set; |
|
| 97 |
-// int new_uuid; |
|
| 98 |
-// int secure_data; |
|
| 99 |
-// int retry_remove; |
|
| 100 |
-// int enable_checks; |
|
| 101 |
-// int expected_errno; |
|
| 102 |
- |
|
| 103 |
-// char *uuid; |
|
| 104 |
-// char *mangled_uuid; |
|
| 105 |
-// }; |
|
| 106 |
-// |
| 107 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,15 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import "C" |
|
| 6 |
- |
|
| 7 |
-// Due to the way cgo works this has to be in a separate file, as devmapper.go has |
|
| 8 |
-// definitions in the cgo block, which is incompatible with using "//export" |
|
| 9 |
- |
|
| 10 |
-//export DevmapperLogCallback |
|
| 11 |
-func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_class C.int, message *C.char) {
|
|
| 12 |
- if dmLogger != nil {
|
|
| 13 |
- dmLogger.log(int(level), C.GoString(file), int(line), int(dm_errno_or_class), C.GoString(message)) |
|
| 14 |
- } |
|
| 15 |
-} |
| 16 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,287 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "testing" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func TestTaskCreate(t *testing.T) {
|
|
| 10 |
- t.Skip("FIXME: not a unit test")
|
|
| 11 |
- // Test success |
|
| 12 |
- taskCreate(t, DeviceInfo) |
|
| 13 |
- |
|
| 14 |
- // Test Failure |
|
| 15 |
- DmTaskCreate = dmTaskCreateFail |
|
| 16 |
- defer func() { DmTaskCreate = dmTaskCreateFct }()
|
|
| 17 |
- if task := TaskCreate(-1); task != nil {
|
|
| 18 |
- t.Fatalf("An error should have occured while creating an invalid task.")
|
|
| 19 |
- } |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-func TestTaskRun(t *testing.T) {
|
|
| 23 |
- t.Skip("FIXME: not a unit test")
|
|
| 24 |
- task := taskCreate(t, DeviceInfo) |
|
| 25 |
- |
|
| 26 |
- // Test success |
|
| 27 |
- // Perform the RUN |
|
| 28 |
- if err := task.Run(); err != nil {
|
|
| 29 |
- t.Fatal(err) |
|
| 30 |
- } |
|
| 31 |
- // Make sure we don't have error with GetInfo |
|
| 32 |
- if _, err := task.GetInfo(); err != nil {
|
|
| 33 |
- t.Fatal(err) |
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 |
- // Test failure |
|
| 37 |
- DmTaskRun = dmTaskRunFail |
|
| 38 |
- defer func() { DmTaskRun = dmTaskRunFct }()
|
|
| 39 |
- |
|
| 40 |
- task = taskCreate(t, DeviceInfo) |
|
| 41 |
- // Perform the RUN |
|
| 42 |
- if err := task.Run(); err != ErrTaskRun {
|
|
| 43 |
- t.Fatalf("An error should have occured while running task.")
|
|
| 44 |
- } |
|
| 45 |
- // Make sure GetInfo also fails |
|
| 46 |
- if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 47 |
- t.Fatalf("GetInfo should fail if task.Run() failed.")
|
|
| 48 |
- } |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-func TestTaskSetName(t *testing.T) {
|
|
| 52 |
- t.Skip("FIXME: not a unit test")
|
|
| 53 |
- task := taskCreate(t, DeviceInfo) |
|
| 54 |
- |
|
| 55 |
- // Test success |
|
| 56 |
- if err := task.SetName("test"); err != nil {
|
|
| 57 |
- t.Fatal(err) |
|
| 58 |
- } |
|
| 59 |
- |
|
| 60 |
- // Test failure |
|
| 61 |
- DmTaskSetName = dmTaskSetNameFail |
|
| 62 |
- defer func() { DmTaskSetName = dmTaskSetNameFct }()
|
|
| 63 |
- |
|
| 64 |
- if err := task.SetName("test"); err != ErrTaskSetName {
|
|
| 65 |
- t.Fatalf("An error should have occured while runnign SetName.")
|
|
| 66 |
- } |
|
| 67 |
-} |
|
| 68 |
- |
|
| 69 |
-func TestTaskSetMessage(t *testing.T) {
|
|
| 70 |
- t.Skip("FIXME: not a unit test")
|
|
| 71 |
- task := taskCreate(t, DeviceInfo) |
|
| 72 |
- |
|
| 73 |
- // Test success |
|
| 74 |
- if err := task.SetMessage("test"); err != nil {
|
|
| 75 |
- t.Fatal(err) |
|
| 76 |
- } |
|
| 77 |
- |
|
| 78 |
- // Test failure |
|
| 79 |
- DmTaskSetMessage = dmTaskSetMessageFail |
|
| 80 |
- defer func() { DmTaskSetMessage = dmTaskSetMessageFct }()
|
|
| 81 |
- |
|
| 82 |
- if err := task.SetMessage("test"); err != ErrTaskSetMessage {
|
|
| 83 |
- t.Fatalf("An error should have occured while runnign SetMessage.")
|
|
| 84 |
- } |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-func TestTaskSetSector(t *testing.T) {
|
|
| 88 |
- t.Skip("FIXME: not a unit test")
|
|
| 89 |
- task := taskCreate(t, DeviceInfo) |
|
| 90 |
- |
|
| 91 |
- // Test success |
|
| 92 |
- if err := task.SetSector(128); err != nil {
|
|
| 93 |
- t.Fatal(err) |
|
| 94 |
- } |
|
| 95 |
- |
|
| 96 |
- DmTaskSetSector = dmTaskSetSectorFail |
|
| 97 |
- defer func() { DmTaskSetSector = dmTaskSetSectorFct }()
|
|
| 98 |
- |
|
| 99 |
- // Test failure |
|
| 100 |
- if err := task.SetSector(0); err != ErrTaskSetSector {
|
|
| 101 |
- t.Fatalf("An error should have occured while running SetSector.")
|
|
| 102 |
- } |
|
| 103 |
-} |
|
| 104 |
- |
|
| 105 |
-func TestTaskSetCookie(t *testing.T) {
|
|
| 106 |
- t.Skip("FIXME: not a unit test")
|
|
| 107 |
- var ( |
|
| 108 |
- cookie uint = 0 |
|
| 109 |
- task = taskCreate(t, DeviceInfo) |
|
| 110 |
- ) |
|
| 111 |
- |
|
| 112 |
- // Test success |
|
| 113 |
- if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 114 |
- t.Fatal(err) |
|
| 115 |
- } |
|
| 116 |
- |
|
| 117 |
- // Test failure |
|
| 118 |
- if err := task.SetCookie(nil, 0); err != ErrNilCookie {
|
|
| 119 |
- t.Fatalf("An error should have occured while running SetCookie with nil cookie.")
|
|
| 120 |
- } |
|
| 121 |
- |
|
| 122 |
- DmTaskSetCookie = dmTaskSetCookieFail |
|
| 123 |
- defer func() { DmTaskSetCookie = dmTaskSetCookieFct }()
|
|
| 124 |
- |
|
| 125 |
- if err := task.SetCookie(&cookie, 0); err != ErrTaskSetCookie {
|
|
| 126 |
- t.Fatalf("An error should have occured while running SetCookie.")
|
|
| 127 |
- } |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-func TestTaskSetAddNode(t *testing.T) {
|
|
| 131 |
- t.Skip("FIXME: not a unit test")
|
|
| 132 |
- task := taskCreate(t, DeviceInfo) |
|
| 133 |
- |
|
| 134 |
- // Test success |
|
| 135 |
- if err := task.SetAddNode(0); err != nil {
|
|
| 136 |
- t.Fatal(err) |
|
| 137 |
- } |
|
| 138 |
- |
|
| 139 |
- // Test failure |
|
| 140 |
- if err := task.SetAddNode(-1); err != ErrInvalidAddNode {
|
|
| 141 |
- t.Fatalf("An error should have occured running SetAddNode with wrong node.")
|
|
| 142 |
- } |
|
| 143 |
- |
|
| 144 |
- DmTaskSetAddNode = dmTaskSetAddNodeFail |
|
| 145 |
- defer func() { DmTaskSetAddNode = dmTaskSetAddNodeFct }()
|
|
| 146 |
- |
|
| 147 |
- if err := task.SetAddNode(0); err != ErrTaskSetAddNode {
|
|
| 148 |
- t.Fatalf("An error should have occured running SetAddNode.")
|
|
| 149 |
- } |
|
| 150 |
-} |
|
| 151 |
- |
|
| 152 |
-func TestTaskSetRo(t *testing.T) {
|
|
| 153 |
- t.Skip("FIXME: not a unit test")
|
|
| 154 |
- task := taskCreate(t, DeviceInfo) |
|
| 155 |
- |
|
| 156 |
- // Test success |
|
| 157 |
- if err := task.SetRo(); err != nil {
|
|
| 158 |
- t.Fatal(err) |
|
| 159 |
- } |
|
| 160 |
- |
|
| 161 |
- // Test failure |
|
| 162 |
- DmTaskSetRo = dmTaskSetRoFail |
|
| 163 |
- defer func() { DmTaskSetRo = dmTaskSetRoFct }()
|
|
| 164 |
- |
|
| 165 |
- if err := task.SetRo(); err != ErrTaskSetRo {
|
|
| 166 |
- t.Fatalf("An error should have occured running SetRo.")
|
|
| 167 |
- } |
|
| 168 |
-} |
|
| 169 |
- |
|
| 170 |
-func TestTaskAddTarget(t *testing.T) {
|
|
| 171 |
- t.Skip("FIXME: not a unit test")
|
|
| 172 |
- task := taskCreate(t, DeviceInfo) |
|
| 173 |
- |
|
| 174 |
- // Test success |
|
| 175 |
- if err := task.AddTarget(0, 128, "thinp", ""); err != nil {
|
|
| 176 |
- t.Fatal(err) |
|
| 177 |
- } |
|
| 178 |
- |
|
| 179 |
- // Test failure |
|
| 180 |
- DmTaskAddTarget = dmTaskAddTargetFail |
|
| 181 |
- defer func() { DmTaskAddTarget = dmTaskAddTargetFct }()
|
|
| 182 |
- |
|
| 183 |
- if err := task.AddTarget(0, 128, "thinp", ""); err != ErrTaskAddTarget {
|
|
| 184 |
- t.Fatalf("An error should have occured running AddTarget.")
|
|
| 185 |
- } |
|
| 186 |
-} |
|
| 187 |
- |
|
| 188 |
-// func TestTaskGetInfo(t *testing.T) {
|
|
| 189 |
-// task := taskCreate(t, DeviceInfo) |
|
| 190 |
- |
|
| 191 |
-// // Test success |
|
| 192 |
-// if _, err := task.GetInfo(); err != nil {
|
|
| 193 |
-// t.Fatal(err) |
|
| 194 |
-// } |
|
| 195 |
- |
|
| 196 |
-// // Test failure |
|
| 197 |
-// DmTaskGetInfo = dmTaskGetInfoFail |
|
| 198 |
-// defer func() { DmTaskGetInfo = dmTaskGetInfoFct }()
|
|
| 199 |
- |
|
| 200 |
-// if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 201 |
-// t.Fatalf("An error should have occured running GetInfo.")
|
|
| 202 |
-// } |
|
| 203 |
-// } |
|
| 204 |
- |
|
| 205 |
-// func TestTaskGetNextTarget(t *testing.T) {
|
|
| 206 |
-// task := taskCreate(t, DeviceInfo) |
|
| 207 |
- |
|
| 208 |
-// if next, _, _, _, _ := task.GetNextTarget(0); next == 0 {
|
|
| 209 |
-// t.Fatalf("The next target should not be 0.")
|
|
| 210 |
-// } |
|
| 211 |
-// } |
|
| 212 |
- |
|
| 213 |
-/// Utils |
|
| 214 |
-func taskCreate(t *testing.T, taskType TaskType) *Task {
|
|
| 215 |
- task := TaskCreate(taskType) |
|
| 216 |
- if task == nil {
|
|
| 217 |
- t.Fatalf("Error creating task")
|
|
| 218 |
- } |
|
| 219 |
- return task |
|
| 220 |
-} |
|
| 221 |
- |
|
| 222 |
-/// Failure function replacement |
|
| 223 |
-func dmTaskCreateFail(t int) *CDmTask {
|
|
| 224 |
- return nil |
|
| 225 |
-} |
|
| 226 |
- |
|
| 227 |
-func dmTaskRunFail(task *CDmTask) int {
|
|
| 228 |
- return -1 |
|
| 229 |
-} |
|
| 230 |
- |
|
| 231 |
-func dmTaskSetNameFail(task *CDmTask, name string) int {
|
|
| 232 |
- return -1 |
|
| 233 |
-} |
|
| 234 |
- |
|
| 235 |
-func dmTaskSetMessageFail(task *CDmTask, message string) int {
|
|
| 236 |
- return -1 |
|
| 237 |
-} |
|
| 238 |
- |
|
| 239 |
-func dmTaskSetSectorFail(task *CDmTask, sector uint64) int {
|
|
| 240 |
- return -1 |
|
| 241 |
-} |
|
| 242 |
- |
|
| 243 |
-func dmTaskSetCookieFail(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 244 |
- return -1 |
|
| 245 |
-} |
|
| 246 |
- |
|
| 247 |
-func dmTaskSetAddNodeFail(task *CDmTask, addNode AddNodeType) int {
|
|
| 248 |
- return -1 |
|
| 249 |
-} |
|
| 250 |
- |
|
| 251 |
-func dmTaskSetRoFail(task *CDmTask) int {
|
|
| 252 |
- return -1 |
|
| 253 |
-} |
|
| 254 |
- |
|
| 255 |
-func dmTaskAddTargetFail(task *CDmTask, |
|
| 256 |
- start, size uint64, ttype, params string) int {
|
|
| 257 |
- return -1 |
|
| 258 |
-} |
|
| 259 |
- |
|
| 260 |
-func dmTaskGetInfoFail(task *CDmTask, info *Info) int {
|
|
| 261 |
- return -1 |
|
| 262 |
-} |
|
| 263 |
- |
|
| 264 |
-func dmGetNextTargetFail(task *CDmTask, next uintptr, start, length *uint64, |
|
| 265 |
- target, params *string) uintptr {
|
|
| 266 |
- return 0 |
|
| 267 |
-} |
|
| 268 |
- |
|
| 269 |
-func dmAttachLoopDeviceFail(filename string, fd *int) string {
|
|
| 270 |
- return "" |
|
| 271 |
-} |
|
| 272 |
- |
|
| 273 |
-func sysGetBlockSizeFail(fd uintptr, size *uint64) sysErrno {
|
|
| 274 |
- return 1 |
|
| 275 |
-} |
|
| 276 |
- |
|
| 277 |
-func dmUdevWaitFail(cookie uint) int {
|
|
| 278 |
- return -1 |
|
| 279 |
-} |
|
| 280 |
- |
|
| 281 |
-func dmSetDevDirFail(dir string) int {
|
|
| 282 |
- return -1 |
|
| 283 |
-} |
|
| 284 |
- |
|
| 285 |
-func dmGetLibraryVersionFail(version *string) int {
|
|
| 286 |
- return -1 |
|
| 287 |
-} |
| 288 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,229 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-/* |
|
| 6 |
-#cgo LDFLAGS: -L. -ldevmapper |
|
| 7 |
-#include <libdevmapper.h> |
|
| 8 |
-#include <linux/loop.h> // FIXME: present only for defines, maybe we can remove it? |
|
| 9 |
-#include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it? |
|
| 10 |
- |
|
| 11 |
-#ifndef LOOP_CTL_GET_FREE |
|
| 12 |
- #define LOOP_CTL_GET_FREE 0x4C82 |
|
| 13 |
-#endif |
|
| 14 |
- |
|
| 15 |
-#ifndef LO_FLAGS_PARTSCAN |
|
| 16 |
- #define LO_FLAGS_PARTSCAN 8 |
|
| 17 |
-#endif |
|
| 18 |
- |
|
| 19 |
-// FIXME: Can't we find a way to do the logging in pure Go? |
|
| 20 |
-extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); |
|
| 21 |
- |
|
| 22 |
-static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...) |
|
| 23 |
-{
|
|
| 24 |
- char buffer[256]; |
|
| 25 |
- va_list ap; |
|
| 26 |
- |
|
| 27 |
- va_start(ap, f); |
|
| 28 |
- vsnprintf(buffer, 256, f, ap); |
|
| 29 |
- va_end(ap); |
|
| 30 |
- |
|
| 31 |
- DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); |
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-static void log_with_errno_init() |
|
| 35 |
-{
|
|
| 36 |
- dm_log_with_errno_init(log_cb); |
|
| 37 |
-} |
|
| 38 |
-*/ |
|
| 39 |
-import "C" |
|
| 40 |
- |
|
| 41 |
-import ( |
|
| 42 |
- "unsafe" |
|
| 43 |
-) |
|
| 44 |
- |
|
| 45 |
-type ( |
|
| 46 |
- CDmTask C.struct_dm_task |
|
| 47 |
- |
|
| 48 |
- CLoopInfo64 C.struct_loop_info64 |
|
| 49 |
- LoopInfo64 struct {
|
|
| 50 |
- loDevice uint64 /* ioctl r/o */ |
|
| 51 |
- loInode uint64 /* ioctl r/o */ |
|
| 52 |
- loRdevice uint64 /* ioctl r/o */ |
|
| 53 |
- loOffset uint64 |
|
| 54 |
- loSizelimit uint64 /* bytes, 0 == max available */ |
|
| 55 |
- loNumber uint32 /* ioctl r/o */ |
|
| 56 |
- loEncrypt_type uint32 |
|
| 57 |
- loEncrypt_key_size uint32 /* ioctl w/o */ |
|
| 58 |
- loFlags uint32 /* ioctl r/o */ |
|
| 59 |
- loFileName [LoNameSize]uint8 |
|
| 60 |
- loCryptName [LoNameSize]uint8 |
|
| 61 |
- loEncryptKey [LoKeySize]uint8 /* ioctl w/o */ |
|
| 62 |
- loInit [2]uint64 |
|
| 63 |
- } |
|
| 64 |
-) |
|
| 65 |
- |
|
| 66 |
-// IOCTL consts |
|
| 67 |
-const ( |
|
| 68 |
- BlkGetSize64 = C.BLKGETSIZE64 |
|
| 69 |
- BlkDiscard = C.BLKDISCARD |
|
| 70 |
- |
|
| 71 |
- LoopSetFd = C.LOOP_SET_FD |
|
| 72 |
- LoopCtlGetFree = C.LOOP_CTL_GET_FREE |
|
| 73 |
- LoopGetStatus64 = C.LOOP_GET_STATUS64 |
|
| 74 |
- LoopSetStatus64 = C.LOOP_SET_STATUS64 |
|
| 75 |
- LoopClrFd = C.LOOP_CLR_FD |
|
| 76 |
- LoopSetCapacity = C.LOOP_SET_CAPACITY |
|
| 77 |
-) |
|
| 78 |
- |
|
| 79 |
-const ( |
|
| 80 |
- LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR |
|
| 81 |
- LoFlagsReadOnly = C.LO_FLAGS_READ_ONLY |
|
| 82 |
- LoFlagsPartScan = C.LO_FLAGS_PARTSCAN |
|
| 83 |
- LoKeySize = C.LO_KEY_SIZE |
|
| 84 |
- LoNameSize = C.LO_NAME_SIZE |
|
| 85 |
-) |
|
| 86 |
- |
|
| 87 |
-var ( |
|
| 88 |
- DmGetLibraryVersion = dmGetLibraryVersionFct |
|
| 89 |
- DmGetNextTarget = dmGetNextTargetFct |
|
| 90 |
- DmLogInitVerbose = dmLogInitVerboseFct |
|
| 91 |
- DmSetDevDir = dmSetDevDirFct |
|
| 92 |
- DmTaskAddTarget = dmTaskAddTargetFct |
|
| 93 |
- DmTaskCreate = dmTaskCreateFct |
|
| 94 |
- DmTaskDestroy = dmTaskDestroyFct |
|
| 95 |
- DmTaskGetInfo = dmTaskGetInfoFct |
|
| 96 |
- DmTaskRun = dmTaskRunFct |
|
| 97 |
- DmTaskSetAddNode = dmTaskSetAddNodeFct |
|
| 98 |
- DmTaskSetCookie = dmTaskSetCookieFct |
|
| 99 |
- DmTaskSetMessage = dmTaskSetMessageFct |
|
| 100 |
- DmTaskSetName = dmTaskSetNameFct |
|
| 101 |
- DmTaskSetRo = dmTaskSetRoFct |
|
| 102 |
- DmTaskSetSector = dmTaskSetSectorFct |
|
| 103 |
- DmUdevWait = dmUdevWaitFct |
|
| 104 |
- LogWithErrnoInit = logWithErrnoInitFct |
|
| 105 |
-) |
|
| 106 |
- |
|
| 107 |
-func free(p *C.char) {
|
|
| 108 |
- C.free(unsafe.Pointer(p)) |
|
| 109 |
-} |
|
| 110 |
- |
|
| 111 |
-func dmTaskDestroyFct(task *CDmTask) {
|
|
| 112 |
- C.dm_task_destroy((*C.struct_dm_task)(task)) |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-func dmTaskCreateFct(taskType int) *CDmTask {
|
|
| 116 |
- return (*CDmTask)(C.dm_task_create(C.int(taskType))) |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 |
-func dmTaskRunFct(task *CDmTask) int {
|
|
| 120 |
- ret, _ := C.dm_task_run((*C.struct_dm_task)(task)) |
|
| 121 |
- return int(ret) |
|
| 122 |
-} |
|
| 123 |
- |
|
| 124 |
-func dmTaskSetNameFct(task *CDmTask, name string) int {
|
|
| 125 |
- Cname := C.CString(name) |
|
| 126 |
- defer free(Cname) |
|
| 127 |
- |
|
| 128 |
- return int(C.dm_task_set_name((*C.struct_dm_task)(task), Cname)) |
|
| 129 |
-} |
|
| 130 |
- |
|
| 131 |
-func dmTaskSetMessageFct(task *CDmTask, message string) int {
|
|
| 132 |
- Cmessage := C.CString(message) |
|
| 133 |
- defer free(Cmessage) |
|
| 134 |
- |
|
| 135 |
- return int(C.dm_task_set_message((*C.struct_dm_task)(task), Cmessage)) |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-func dmTaskSetSectorFct(task *CDmTask, sector uint64) int {
|
|
| 139 |
- return int(C.dm_task_set_sector((*C.struct_dm_task)(task), C.uint64_t(sector))) |
|
| 140 |
-} |
|
| 141 |
- |
|
| 142 |
-func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 143 |
- cCookie := C.uint32_t(*cookie) |
|
| 144 |
- defer func() {
|
|
| 145 |
- *cookie = uint(cCookie) |
|
| 146 |
- }() |
|
| 147 |
- return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, C.uint16_t(flags))) |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int {
|
|
| 151 |
- return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode))) |
|
| 152 |
-} |
|
| 153 |
- |
|
| 154 |
-func dmTaskSetRoFct(task *CDmTask) int {
|
|
| 155 |
- return int(C.dm_task_set_ro((*C.struct_dm_task)(task))) |
|
| 156 |
-} |
|
| 157 |
- |
|
| 158 |
-func dmTaskAddTargetFct(task *CDmTask, |
|
| 159 |
- start, size uint64, ttype, params string) int {
|
|
| 160 |
- |
|
| 161 |
- Cttype := C.CString(ttype) |
|
| 162 |
- defer free(Cttype) |
|
| 163 |
- |
|
| 164 |
- Cparams := C.CString(params) |
|
| 165 |
- defer free(Cparams) |
|
| 166 |
- |
|
| 167 |
- return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) |
|
| 168 |
-} |
|
| 169 |
- |
|
| 170 |
-func dmTaskGetInfoFct(task *CDmTask, info *Info) int {
|
|
| 171 |
- Cinfo := C.struct_dm_info{}
|
|
| 172 |
- defer func() {
|
|
| 173 |
- info.Exists = int(Cinfo.exists) |
|
| 174 |
- info.Suspended = int(Cinfo.suspended) |
|
| 175 |
- info.LiveTable = int(Cinfo.live_table) |
|
| 176 |
- info.InactiveTable = int(Cinfo.inactive_table) |
|
| 177 |
- info.OpenCount = int32(Cinfo.open_count) |
|
| 178 |
- info.EventNr = uint32(Cinfo.event_nr) |
|
| 179 |
- info.Major = uint32(Cinfo.major) |
|
| 180 |
- info.Minor = uint32(Cinfo.minor) |
|
| 181 |
- info.ReadOnly = int(Cinfo.read_only) |
|
| 182 |
- info.TargetCount = int32(Cinfo.target_count) |
|
| 183 |
- }() |
|
| 184 |
- return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) |
|
| 185 |
-} |
|
| 186 |
- |
|
| 187 |
-func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
|
|
| 188 |
- var ( |
|
| 189 |
- Cstart, Clength C.uint64_t |
|
| 190 |
- CtargetType, Cparams *C.char |
|
| 191 |
- ) |
|
| 192 |
- defer func() {
|
|
| 193 |
- *start = uint64(Cstart) |
|
| 194 |
- *length = uint64(Clength) |
|
| 195 |
- *target = C.GoString(CtargetType) |
|
| 196 |
- *params = C.GoString(Cparams) |
|
| 197 |
- }() |
|
| 198 |
- |
|
| 199 |
- nextp := C.dm_get_next_target((*C.struct_dm_task)(task), unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) |
|
| 200 |
- return uintptr(nextp) |
|
| 201 |
-} |
|
| 202 |
- |
|
| 203 |
-func dmUdevWaitFct(cookie uint) int {
|
|
| 204 |
- return int(C.dm_udev_wait(C.uint32_t(cookie))) |
|
| 205 |
-} |
|
| 206 |
- |
|
| 207 |
-func dmLogInitVerboseFct(level int) {
|
|
| 208 |
- C.dm_log_init_verbose(C.int(level)) |
|
| 209 |
-} |
|
| 210 |
- |
|
| 211 |
-func logWithErrnoInitFct() {
|
|
| 212 |
- C.log_with_errno_init() |
|
| 213 |
-} |
|
| 214 |
- |
|
| 215 |
-func dmSetDevDirFct(dir string) int {
|
|
| 216 |
- Cdir := C.CString(dir) |
|
| 217 |
- defer free(Cdir) |
|
| 218 |
- |
|
| 219 |
- return int(C.dm_set_dev_dir(Cdir)) |
|
| 220 |
-} |
|
| 221 |
- |
|
| 222 |
-func dmGetLibraryVersionFct(version *string) int {
|
|
| 223 |
- buffer := C.CString(string(make([]byte, 128))) |
|
| 224 |
- defer free(buffer) |
|
| 225 |
- defer func() {
|
|
| 226 |
- *version = C.GoString(buffer) |
|
| 227 |
- }() |
|
| 228 |
- return int(C.dm_get_library_version(buffer, 128)) |
|
| 229 |
-} |
| 230 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,143 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "fmt" |
|
| 7 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 8 |
- "github.com/dotcloud/docker/utils" |
|
| 9 |
- "io/ioutil" |
|
| 10 |
- "os" |
|
| 11 |
- "path" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-func init() {
|
|
| 15 |
- graphdriver.Register("devicemapper", Init)
|
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-// Placeholder interfaces, to be replaced |
|
| 19 |
-// at integration. |
|
| 20 |
- |
|
| 21 |
-// End of placeholder interfaces. |
|
| 22 |
- |
|
| 23 |
-type Driver struct {
|
|
| 24 |
- *DeviceSet |
|
| 25 |
- home string |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-var Init = func(home string) (graphdriver.Driver, error) {
|
|
| 29 |
- deviceSet, err := NewDeviceSet(home, true) |
|
| 30 |
- if err != nil {
|
|
| 31 |
- return nil, err |
|
| 32 |
- } |
|
| 33 |
- d := &Driver{
|
|
| 34 |
- DeviceSet: deviceSet, |
|
| 35 |
- home: home, |
|
| 36 |
- } |
|
| 37 |
- return d, nil |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (d *Driver) String() string {
|
|
| 41 |
- return "devicemapper" |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-func (d *Driver) Status() [][2]string {
|
|
| 45 |
- s := d.DeviceSet.Status() |
|
| 46 |
- |
|
| 47 |
- status := [][2]string{
|
|
| 48 |
- {"Pool Name", s.PoolName},
|
|
| 49 |
- {"Data file", s.DataLoopback},
|
|
| 50 |
- {"Metadata file", s.MetadataLoopback},
|
|
| 51 |
- {"Data Space Used", fmt.Sprintf("%.1f Mb", float64(s.Data.Used)/(1024*1024))},
|
|
| 52 |
- {"Data Space Total", fmt.Sprintf("%.1f Mb", float64(s.Data.Total)/(1024*1024))},
|
|
| 53 |
- {"Metadata Space Used", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Used)/(1024*1024))},
|
|
| 54 |
- {"Metadata Space Total", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Total)/(1024*1024))},
|
|
| 55 |
- } |
|
| 56 |
- return status |
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-func (d *Driver) Cleanup() error {
|
|
| 60 |
- return d.DeviceSet.Shutdown() |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-func (d *Driver) Create(id, parent string) error {
|
|
| 64 |
- if err := d.DeviceSet.AddDevice(id, parent); err != nil {
|
|
| 65 |
- return err |
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- mp := path.Join(d.home, "mnt", id) |
|
| 69 |
- if err := d.mount(id, mp); err != nil {
|
|
| 70 |
- return err |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- if err := osMkdirAll(path.Join(mp, "rootfs"), 0755); err != nil && !osIsExist(err) {
|
|
| 74 |
- return err |
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
- // Create an "id" file with the container/image id in it to help reconscruct this in case |
|
| 78 |
- // of later problems |
|
| 79 |
- if err := ioutil.WriteFile(path.Join(mp, "id"), []byte(id), 0600); err != nil {
|
|
| 80 |
- return err |
|
| 81 |
- } |
|
| 82 |
- |
|
| 83 |
- // We float this reference so that the next Get call can |
|
| 84 |
- // steal it, so we don't have to unmount |
|
| 85 |
- if err := d.DeviceSet.UnmountDevice(id, UnmountFloat); err != nil {
|
|
| 86 |
- return err |
|
| 87 |
- } |
|
| 88 |
- |
|
| 89 |
- return nil |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-func (d *Driver) Remove(id string) error {
|
|
| 93 |
- if !d.DeviceSet.HasDevice(id) {
|
|
| 94 |
- // Consider removing a non-existing device a no-op |
|
| 95 |
- // This is useful to be able to progress on container removal |
|
| 96 |
- // if the underlying device has gone away due to earlier errors |
|
| 97 |
- return nil |
|
| 98 |
- } |
|
| 99 |
- |
|
| 100 |
- // Sink the float from create in case no Get() call was made |
|
| 101 |
- if err := d.DeviceSet.UnmountDevice(id, UnmountSink); err != nil {
|
|
| 102 |
- return err |
|
| 103 |
- } |
|
| 104 |
- // This assumes the device has been properly Get/Put:ed and thus is unmounted |
|
| 105 |
- if err := d.DeviceSet.DeleteDevice(id); err != nil {
|
|
| 106 |
- return err |
|
| 107 |
- } |
|
| 108 |
- |
|
| 109 |
- mp := path.Join(d.home, "mnt", id) |
|
| 110 |
- if err := os.RemoveAll(mp); err != nil && !os.IsNotExist(err) {
|
|
| 111 |
- return err |
|
| 112 |
- } |
|
| 113 |
- |
|
| 114 |
- return nil |
|
| 115 |
-} |
|
| 116 |
- |
|
| 117 |
-func (d *Driver) Get(id string) (string, error) {
|
|
| 118 |
- mp := path.Join(d.home, "mnt", id) |
|
| 119 |
- if err := d.mount(id, mp); err != nil {
|
|
| 120 |
- return "", err |
|
| 121 |
- } |
|
| 122 |
- |
|
| 123 |
- return path.Join(mp, "rootfs"), nil |
|
| 124 |
-} |
|
| 125 |
- |
|
| 126 |
-func (d *Driver) Put(id string) {
|
|
| 127 |
- if err := d.DeviceSet.UnmountDevice(id, UnmountRegular); err != nil {
|
|
| 128 |
- utils.Errorf("Warning: error unmounting device %s: %s\n", id, err)
|
|
| 129 |
- } |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func (d *Driver) mount(id, mountPoint string) error {
|
|
| 133 |
- // Create the target directories if they don't exist |
|
| 134 |
- if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) {
|
|
| 135 |
- return err |
|
| 136 |
- } |
|
| 137 |
- // Mount the device |
|
| 138 |
- return d.DeviceSet.MountDevice(id, mountPoint) |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func (d *Driver) Exists(id string) bool {
|
|
| 142 |
- return d.Devices[id] != nil |
|
| 143 |
-} |
| 144 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,886 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "fmt" |
|
| 7 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 8 |
- "io/ioutil" |
|
| 9 |
- "path" |
|
| 10 |
- "runtime" |
|
| 11 |
- "strings" |
|
| 12 |
- "syscall" |
|
| 13 |
- "testing" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-func init() {
|
|
| 17 |
- // Reduce the size the the base fs and loopback for the tests |
|
| 18 |
- DefaultDataLoopbackSize = 300 * 1024 * 1024 |
|
| 19 |
- DefaultMetaDataLoopbackSize = 200 * 1024 * 1024 |
|
| 20 |
- DefaultBaseFsSize = 300 * 1024 * 1024 |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-// denyAllDevmapper mocks all calls to libdevmapper in the unit tests, and denies them by default |
|
| 24 |
-func denyAllDevmapper() {
|
|
| 25 |
- // Hijack all calls to libdevmapper with default panics. |
|
| 26 |
- // Authorized calls are selectively hijacked in each tests. |
|
| 27 |
- DmTaskCreate = func(t int) *CDmTask {
|
|
| 28 |
- panic("DmTaskCreate: this method should not be called here")
|
|
| 29 |
- } |
|
| 30 |
- DmTaskRun = func(task *CDmTask) int {
|
|
| 31 |
- panic("DmTaskRun: this method should not be called here")
|
|
| 32 |
- } |
|
| 33 |
- DmTaskSetName = func(task *CDmTask, name string) int {
|
|
| 34 |
- panic("DmTaskSetName: this method should not be called here")
|
|
| 35 |
- } |
|
| 36 |
- DmTaskSetMessage = func(task *CDmTask, message string) int {
|
|
| 37 |
- panic("DmTaskSetMessage: this method should not be called here")
|
|
| 38 |
- } |
|
| 39 |
- DmTaskSetSector = func(task *CDmTask, sector uint64) int {
|
|
| 40 |
- panic("DmTaskSetSector: this method should not be called here")
|
|
| 41 |
- } |
|
| 42 |
- DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 43 |
- panic("DmTaskSetCookie: this method should not be called here")
|
|
| 44 |
- } |
|
| 45 |
- DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
|
|
| 46 |
- panic("DmTaskSetAddNode: this method should not be called here")
|
|
| 47 |
- } |
|
| 48 |
- DmTaskSetRo = func(task *CDmTask) int {
|
|
| 49 |
- panic("DmTaskSetRo: this method should not be called here")
|
|
| 50 |
- } |
|
| 51 |
- DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
|
|
| 52 |
- panic("DmTaskAddTarget: this method should not be called here")
|
|
| 53 |
- } |
|
| 54 |
- DmTaskGetInfo = func(task *CDmTask, info *Info) int {
|
|
| 55 |
- panic("DmTaskGetInfo: this method should not be called here")
|
|
| 56 |
- } |
|
| 57 |
- DmGetNextTarget = func(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
|
|
| 58 |
- panic("DmGetNextTarget: this method should not be called here")
|
|
| 59 |
- } |
|
| 60 |
- DmUdevWait = func(cookie uint) int {
|
|
| 61 |
- panic("DmUdevWait: this method should not be called here")
|
|
| 62 |
- } |
|
| 63 |
- DmSetDevDir = func(dir string) int {
|
|
| 64 |
- panic("DmSetDevDir: this method should not be called here")
|
|
| 65 |
- } |
|
| 66 |
- DmGetLibraryVersion = func(version *string) int {
|
|
| 67 |
- panic("DmGetLibraryVersion: this method should not be called here")
|
|
| 68 |
- } |
|
| 69 |
- DmLogInitVerbose = func(level int) {
|
|
| 70 |
- panic("DmLogInitVerbose: this method should not be called here")
|
|
| 71 |
- } |
|
| 72 |
- DmTaskDestroy = func(task *CDmTask) {
|
|
| 73 |
- panic("DmTaskDestroy: this method should not be called here")
|
|
| 74 |
- } |
|
| 75 |
- LogWithErrnoInit = func() {
|
|
| 76 |
- panic("LogWithErrnoInit: this method should not be called here")
|
|
| 77 |
- } |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-func denyAllSyscall() {
|
|
| 81 |
- sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
|
|
| 82 |
- panic("sysMount: this method should not be called here")
|
|
| 83 |
- } |
|
| 84 |
- sysUnmount = func(target string, flags int) (err error) {
|
|
| 85 |
- panic("sysUnmount: this method should not be called here")
|
|
| 86 |
- } |
|
| 87 |
- sysCloseOnExec = func(fd int) {
|
|
| 88 |
- panic("sysCloseOnExec: this method should not be called here")
|
|
| 89 |
- } |
|
| 90 |
- sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
|
| 91 |
- panic("sysSyscall: this method should not be called here")
|
|
| 92 |
- } |
|
| 93 |
- // Not a syscall, but forbidding it here anyway |
|
| 94 |
- Mounted = func(mnt string) (bool, error) {
|
|
| 95 |
- panic("devmapper.Mounted: this method should not be called here")
|
|
| 96 |
- } |
|
| 97 |
- // osOpenFile = os.OpenFile |
|
| 98 |
- // osNewFile = os.NewFile |
|
| 99 |
- // osCreate = os.Create |
|
| 100 |
- // osStat = os.Stat |
|
| 101 |
- // osIsNotExist = os.IsNotExist |
|
| 102 |
- // osIsExist = os.IsExist |
|
| 103 |
- // osMkdirAll = os.MkdirAll |
|
| 104 |
- // osRemoveAll = os.RemoveAll |
|
| 105 |
- // osRename = os.Rename |
|
| 106 |
- // osReadlink = os.Readlink |
|
| 107 |
- |
|
| 108 |
- // execRun = func(name string, args ...string) error {
|
|
| 109 |
- // return exec.Command(name, args...).Run() |
|
| 110 |
- // } |
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-func mkTestDirectory(t *testing.T) string {
|
|
| 114 |
- dir, err := ioutil.TempDir("", "docker-test-devmapper-")
|
|
| 115 |
- if err != nil {
|
|
| 116 |
- t.Fatal(err) |
|
| 117 |
- } |
|
| 118 |
- return dir |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 |
-func newDriver(t *testing.T) *Driver {
|
|
| 122 |
- home := mkTestDirectory(t) |
|
| 123 |
- d, err := Init(home) |
|
| 124 |
- if err != nil {
|
|
| 125 |
- t.Fatal(err) |
|
| 126 |
- } |
|
| 127 |
- return d.(*Driver) |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-func cleanup(d *Driver) {
|
|
| 131 |
- d.Cleanup() |
|
| 132 |
- osRemoveAll(d.home) |
|
| 133 |
-} |
|
| 134 |
- |
|
| 135 |
-type Set map[string]bool |
|
| 136 |
- |
|
| 137 |
-func (r Set) Assert(t *testing.T, names ...string) {
|
|
| 138 |
- for _, key := range names {
|
|
| 139 |
- required := true |
|
| 140 |
- if strings.HasPrefix(key, "?") {
|
|
| 141 |
- key = key[1:] |
|
| 142 |
- required = false |
|
| 143 |
- } |
|
| 144 |
- if _, exists := r[key]; !exists && required {
|
|
| 145 |
- t.Fatalf("Key not set: %s", key)
|
|
| 146 |
- } |
|
| 147 |
- delete(r, key) |
|
| 148 |
- } |
|
| 149 |
- if len(r) != 0 {
|
|
| 150 |
- t.Fatalf("Unexpected keys: %v", r)
|
|
| 151 |
- } |
|
| 152 |
-} |
|
| 153 |
- |
|
| 154 |
-func TestInit(t *testing.T) {
|
|
| 155 |
- var ( |
|
| 156 |
- calls = make(Set) |
|
| 157 |
- taskMessages = make(Set) |
|
| 158 |
- taskTypes = make(Set) |
|
| 159 |
- home = mkTestDirectory(t) |
|
| 160 |
- ) |
|
| 161 |
- defer osRemoveAll(home) |
|
| 162 |
- |
|
| 163 |
- func() {
|
|
| 164 |
- denyAllDevmapper() |
|
| 165 |
- DmSetDevDir = func(dir string) int {
|
|
| 166 |
- calls["DmSetDevDir"] = true |
|
| 167 |
- expectedDir := "/dev" |
|
| 168 |
- if dir != expectedDir {
|
|
| 169 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmSetDevDir(%v)\nReceived: DmSetDevDir(%v)\n", expectedDir, dir)
|
|
| 170 |
- } |
|
| 171 |
- return 0 |
|
| 172 |
- } |
|
| 173 |
- LogWithErrnoInit = func() {
|
|
| 174 |
- calls["DmLogWithErrnoInit"] = true |
|
| 175 |
- } |
|
| 176 |
- var task1 CDmTask |
|
| 177 |
- DmTaskCreate = func(taskType int) *CDmTask {
|
|
| 178 |
- calls["DmTaskCreate"] = true |
|
| 179 |
- taskTypes[fmt.Sprintf("%d", taskType)] = true
|
|
| 180 |
- return &task1 |
|
| 181 |
- } |
|
| 182 |
- DmTaskSetName = func(task *CDmTask, name string) int {
|
|
| 183 |
- calls["DmTaskSetName"] = true |
|
| 184 |
- expectedTask := &task1 |
|
| 185 |
- if task != expectedTask {
|
|
| 186 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", expectedTask, task)
|
|
| 187 |
- } |
|
| 188 |
- // FIXME: use Set.AssertRegexp() |
|
| 189 |
- if !strings.HasPrefix(name, "docker-") && !strings.HasPrefix(name, "/dev/mapper/docker-") || |
|
| 190 |
- !strings.HasSuffix(name, "-pool") && !strings.HasSuffix(name, "-base") {
|
|
| 191 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", "docker-...-pool", name)
|
|
| 192 |
- } |
|
| 193 |
- return 1 |
|
| 194 |
- } |
|
| 195 |
- DmTaskRun = func(task *CDmTask) int {
|
|
| 196 |
- calls["DmTaskRun"] = true |
|
| 197 |
- expectedTask := &task1 |
|
| 198 |
- if task != expectedTask {
|
|
| 199 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskRun(%v)\nReceived: DmTaskRun(%v)\n", expectedTask, task)
|
|
| 200 |
- } |
|
| 201 |
- return 1 |
|
| 202 |
- } |
|
| 203 |
- DmTaskGetInfo = func(task *CDmTask, info *Info) int {
|
|
| 204 |
- calls["DmTaskGetInfo"] = true |
|
| 205 |
- expectedTask := &task1 |
|
| 206 |
- if task != expectedTask {
|
|
| 207 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskGetInfo(%v)\nReceived: DmTaskGetInfo(%v)\n", expectedTask, task)
|
|
| 208 |
- } |
|
| 209 |
- // This will crash if info is not dereferenceable |
|
| 210 |
- info.Exists = 0 |
|
| 211 |
- return 1 |
|
| 212 |
- } |
|
| 213 |
- DmTaskSetSector = func(task *CDmTask, sector uint64) int {
|
|
| 214 |
- calls["DmTaskSetSector"] = true |
|
| 215 |
- expectedTask := &task1 |
|
| 216 |
- if task != expectedTask {
|
|
| 217 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task)
|
|
| 218 |
- } |
|
| 219 |
- if expectedSector := uint64(0); sector != expectedSector {
|
|
| 220 |
- t.Fatalf("Wrong libdevmapper call to DmTaskSetSector\nExpected: %v\nReceived: %v\n", expectedSector, sector)
|
|
| 221 |
- } |
|
| 222 |
- return 1 |
|
| 223 |
- } |
|
| 224 |
- DmTaskSetMessage = func(task *CDmTask, message string) int {
|
|
| 225 |
- calls["DmTaskSetMessage"] = true |
|
| 226 |
- expectedTask := &task1 |
|
| 227 |
- if task != expectedTask {
|
|
| 228 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task)
|
|
| 229 |
- } |
|
| 230 |
- taskMessages[message] = true |
|
| 231 |
- return 1 |
|
| 232 |
- } |
|
| 233 |
- DmTaskDestroy = func(task *CDmTask) {
|
|
| 234 |
- calls["DmTaskDestroy"] = true |
|
| 235 |
- expectedTask := &task1 |
|
| 236 |
- if task != expectedTask {
|
|
| 237 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
|
|
| 238 |
- } |
|
| 239 |
- } |
|
| 240 |
- DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
|
|
| 241 |
- calls["DmTaskSetTarget"] = true |
|
| 242 |
- expectedTask := &task1 |
|
| 243 |
- if task != expectedTask {
|
|
| 244 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
|
|
| 245 |
- } |
|
| 246 |
- if start != 0 {
|
|
| 247 |
- t.Fatalf("Wrong start: %d != %d", start, 0)
|
|
| 248 |
- } |
|
| 249 |
- if ttype != "thin" && ttype != "thin-pool" {
|
|
| 250 |
- t.Fatalf("Wrong ttype: %s", ttype)
|
|
| 251 |
- } |
|
| 252 |
- // Quick smoke test |
|
| 253 |
- if params == "" {
|
|
| 254 |
- t.Fatalf("Params should not be empty")
|
|
| 255 |
- } |
|
| 256 |
- return 1 |
|
| 257 |
- } |
|
| 258 |
- fakeCookie := uint(4321) |
|
| 259 |
- DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 260 |
- calls["DmTaskSetCookie"] = true |
|
| 261 |
- expectedTask := &task1 |
|
| 262 |
- if task != expectedTask {
|
|
| 263 |
- t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
|
|
| 264 |
- } |
|
| 265 |
- if flags != 0 {
|
|
| 266 |
- t.Fatalf("Cookie flags should be 0 (not %x)", flags)
|
|
| 267 |
- } |
|
| 268 |
- *cookie = fakeCookie |
|
| 269 |
- return 1 |
|
| 270 |
- } |
|
| 271 |
- DmUdevWait = func(cookie uint) int {
|
|
| 272 |
- calls["DmUdevWait"] = true |
|
| 273 |
- if cookie != fakeCookie {
|
|
| 274 |
- t.Fatalf("Wrong cookie: %d != %d", cookie, fakeCookie)
|
|
| 275 |
- } |
|
| 276 |
- return 1 |
|
| 277 |
- } |
|
| 278 |
- DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
|
|
| 279 |
- if addNode != AddNodeOnCreate {
|
|
| 280 |
- t.Fatalf("Wrong AddNoteType: %v (expected %v)", addNode, AddNodeOnCreate)
|
|
| 281 |
- } |
|
| 282 |
- calls["DmTaskSetAddNode"] = true |
|
| 283 |
- return 1 |
|
| 284 |
- } |
|
| 285 |
- execRun = func(name string, args ...string) error {
|
|
| 286 |
- calls["execRun"] = true |
|
| 287 |
- if name != "mkfs.ext4" {
|
|
| 288 |
- t.Fatalf("Expected %s to be executed, not %s", "mkfs.ext4", name)
|
|
| 289 |
- } |
|
| 290 |
- return nil |
|
| 291 |
- } |
|
| 292 |
- driver, err := Init(home) |
|
| 293 |
- if err != nil {
|
|
| 294 |
- t.Fatal(err) |
|
| 295 |
- } |
|
| 296 |
- defer func() {
|
|
| 297 |
- if err := driver.Cleanup(); err != nil {
|
|
| 298 |
- t.Fatal(err) |
|
| 299 |
- } |
|
| 300 |
- }() |
|
| 301 |
- }() |
|
| 302 |
- // Put all tests in a function to make sure the garbage collection will |
|
| 303 |
- // occur. |
|
| 304 |
- |
|
| 305 |
- // Call GC to cleanup runtime.Finalizers |
|
| 306 |
- runtime.GC() |
|
| 307 |
- |
|
| 308 |
- calls.Assert(t, |
|
| 309 |
- "DmSetDevDir", |
|
| 310 |
- "DmLogWithErrnoInit", |
|
| 311 |
- "DmTaskSetName", |
|
| 312 |
- "DmTaskRun", |
|
| 313 |
- "DmTaskGetInfo", |
|
| 314 |
- "DmTaskDestroy", |
|
| 315 |
- "execRun", |
|
| 316 |
- "DmTaskCreate", |
|
| 317 |
- "DmTaskSetTarget", |
|
| 318 |
- "DmTaskSetCookie", |
|
| 319 |
- "DmUdevWait", |
|
| 320 |
- "DmTaskSetSector", |
|
| 321 |
- "DmTaskSetMessage", |
|
| 322 |
- "DmTaskSetAddNode", |
|
| 323 |
- ) |
|
| 324 |
- taskTypes.Assert(t, "0", "6", "17") |
|
| 325 |
- taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1") |
|
| 326 |
-} |
|
| 327 |
- |
|
| 328 |
-func fakeInit() func(home string) (graphdriver.Driver, error) {
|
|
| 329 |
- oldInit := Init |
|
| 330 |
- Init = func(home string) (graphdriver.Driver, error) {
|
|
| 331 |
- return &Driver{
|
|
| 332 |
- home: home, |
|
| 333 |
- }, nil |
|
| 334 |
- } |
|
| 335 |
- return oldInit |
|
| 336 |
-} |
|
| 337 |
- |
|
| 338 |
-func restoreInit(init func(home string) (graphdriver.Driver, error)) {
|
|
| 339 |
- Init = init |
|
| 340 |
-} |
|
| 341 |
- |
|
| 342 |
-func mockAllDevmapper(calls Set) {
|
|
| 343 |
- DmSetDevDir = func(dir string) int {
|
|
| 344 |
- calls["DmSetDevDir"] = true |
|
| 345 |
- return 0 |
|
| 346 |
- } |
|
| 347 |
- LogWithErrnoInit = func() {
|
|
| 348 |
- calls["DmLogWithErrnoInit"] = true |
|
| 349 |
- } |
|
| 350 |
- DmTaskCreate = func(taskType int) *CDmTask {
|
|
| 351 |
- calls["DmTaskCreate"] = true |
|
| 352 |
- return &CDmTask{}
|
|
| 353 |
- } |
|
| 354 |
- DmTaskSetName = func(task *CDmTask, name string) int {
|
|
| 355 |
- calls["DmTaskSetName"] = true |
|
| 356 |
- return 1 |
|
| 357 |
- } |
|
| 358 |
- DmTaskRun = func(task *CDmTask) int {
|
|
| 359 |
- calls["DmTaskRun"] = true |
|
| 360 |
- return 1 |
|
| 361 |
- } |
|
| 362 |
- DmTaskGetInfo = func(task *CDmTask, info *Info) int {
|
|
| 363 |
- calls["DmTaskGetInfo"] = true |
|
| 364 |
- return 1 |
|
| 365 |
- } |
|
| 366 |
- DmTaskSetSector = func(task *CDmTask, sector uint64) int {
|
|
| 367 |
- calls["DmTaskSetSector"] = true |
|
| 368 |
- return 1 |
|
| 369 |
- } |
|
| 370 |
- DmTaskSetMessage = func(task *CDmTask, message string) int {
|
|
| 371 |
- calls["DmTaskSetMessage"] = true |
|
| 372 |
- return 1 |
|
| 373 |
- } |
|
| 374 |
- DmTaskDestroy = func(task *CDmTask) {
|
|
| 375 |
- calls["DmTaskDestroy"] = true |
|
| 376 |
- } |
|
| 377 |
- DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
|
|
| 378 |
- calls["DmTaskSetTarget"] = true |
|
| 379 |
- return 1 |
|
| 380 |
- } |
|
| 381 |
- DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 382 |
- calls["DmTaskSetCookie"] = true |
|
| 383 |
- return 1 |
|
| 384 |
- } |
|
| 385 |
- DmUdevWait = func(cookie uint) int {
|
|
| 386 |
- calls["DmUdevWait"] = true |
|
| 387 |
- return 1 |
|
| 388 |
- } |
|
| 389 |
- DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
|
|
| 390 |
- calls["DmTaskSetAddNode"] = true |
|
| 391 |
- return 1 |
|
| 392 |
- } |
|
| 393 |
- execRun = func(name string, args ...string) error {
|
|
| 394 |
- calls["execRun"] = true |
|
| 395 |
- return nil |
|
| 396 |
- } |
|
| 397 |
-} |
|
| 398 |
- |
|
| 399 |
-func TestDriverName(t *testing.T) {
|
|
| 400 |
- denyAllDevmapper() |
|
| 401 |
- defer denyAllDevmapper() |
|
| 402 |
- |
|
| 403 |
- oldInit := fakeInit() |
|
| 404 |
- defer restoreInit(oldInit) |
|
| 405 |
- |
|
| 406 |
- d := newDriver(t) |
|
| 407 |
- if d.String() != "devicemapper" {
|
|
| 408 |
- t.Fatalf("Expected driver name to be devicemapper got %s", d.String())
|
|
| 409 |
- } |
|
| 410 |
-} |
|
| 411 |
- |
|
| 412 |
-func TestDriverCreate(t *testing.T) {
|
|
| 413 |
- denyAllDevmapper() |
|
| 414 |
- denyAllSyscall() |
|
| 415 |
- defer denyAllSyscall() |
|
| 416 |
- defer denyAllDevmapper() |
|
| 417 |
- |
|
| 418 |
- calls := make(Set) |
|
| 419 |
- mockAllDevmapper(calls) |
|
| 420 |
- |
|
| 421 |
- sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
|
|
| 422 |
- calls["sysMount"] = true |
|
| 423 |
- // FIXME: compare the exact source and target strings (inodes + devname) |
|
| 424 |
- if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) {
|
|
| 425 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source)
|
|
| 426 |
- } |
|
| 427 |
- if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
|
|
| 428 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
|
|
| 429 |
- } |
|
| 430 |
- if expectedFstype := "ext4"; fstype != expectedFstype {
|
|
| 431 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype)
|
|
| 432 |
- } |
|
| 433 |
- if expectedFlags := uintptr(3236757504); flags != expectedFlags {
|
|
| 434 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
|
|
| 435 |
- } |
|
| 436 |
- return nil |
|
| 437 |
- } |
|
| 438 |
- |
|
| 439 |
- Mounted = func(mnt string) (bool, error) {
|
|
| 440 |
- calls["Mounted"] = true |
|
| 441 |
- if !strings.HasPrefix(mnt, "/tmp/docker-test-devmapper-") || !strings.HasSuffix(mnt, "/mnt/1") {
|
|
| 442 |
- t.Fatalf("Wrong mounted call\nExpected: Mounted(%v)\nReceived: Mounted(%v)\n", "/tmp/docker-test-devmapper-.../mnt/1", mnt)
|
|
| 443 |
- } |
|
| 444 |
- return false, nil |
|
| 445 |
- } |
|
| 446 |
- |
|
| 447 |
- sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
|
| 448 |
- calls["sysSyscall"] = true |
|
| 449 |
- if trap != sysSysIoctl {
|
|
| 450 |
- t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap)
|
|
| 451 |
- } |
|
| 452 |
- switch a2 {
|
|
| 453 |
- case LoopSetFd: |
|
| 454 |
- calls["ioctl.loopsetfd"] = true |
|
| 455 |
- case LoopCtlGetFree: |
|
| 456 |
- calls["ioctl.loopctlgetfree"] = true |
|
| 457 |
- case LoopGetStatus64: |
|
| 458 |
- calls["ioctl.loopgetstatus"] = true |
|
| 459 |
- case LoopSetStatus64: |
|
| 460 |
- calls["ioctl.loopsetstatus"] = true |
|
| 461 |
- case LoopClrFd: |
|
| 462 |
- calls["ioctl.loopclrfd"] = true |
|
| 463 |
- case LoopSetCapacity: |
|
| 464 |
- calls["ioctl.loopsetcapacity"] = true |
|
| 465 |
- case BlkGetSize64: |
|
| 466 |
- calls["ioctl.blkgetsize"] = true |
|
| 467 |
- default: |
|
| 468 |
- t.Fatalf("Unexpected IOCTL. Received %d", a2)
|
|
| 469 |
- } |
|
| 470 |
- return 0, 0, 0 |
|
| 471 |
- } |
|
| 472 |
- |
|
| 473 |
- func() {
|
|
| 474 |
- d := newDriver(t) |
|
| 475 |
- |
|
| 476 |
- calls.Assert(t, |
|
| 477 |
- "DmSetDevDir", |
|
| 478 |
- "DmLogWithErrnoInit", |
|
| 479 |
- "DmTaskSetName", |
|
| 480 |
- "DmTaskRun", |
|
| 481 |
- "DmTaskGetInfo", |
|
| 482 |
- "execRun", |
|
| 483 |
- "DmTaskCreate", |
|
| 484 |
- "DmTaskSetTarget", |
|
| 485 |
- "DmTaskSetCookie", |
|
| 486 |
- "DmUdevWait", |
|
| 487 |
- "DmTaskSetSector", |
|
| 488 |
- "DmTaskSetMessage", |
|
| 489 |
- "DmTaskSetAddNode", |
|
| 490 |
- "sysSyscall", |
|
| 491 |
- "ioctl.blkgetsize", |
|
| 492 |
- "ioctl.loopsetfd", |
|
| 493 |
- "ioctl.loopsetstatus", |
|
| 494 |
- "?ioctl.loopctlgetfree", |
|
| 495 |
- ) |
|
| 496 |
- |
|
| 497 |
- if err := d.Create("1", ""); err != nil {
|
|
| 498 |
- t.Fatal(err) |
|
| 499 |
- } |
|
| 500 |
- calls.Assert(t, |
|
| 501 |
- "DmTaskCreate", |
|
| 502 |
- "DmTaskGetInfo", |
|
| 503 |
- "sysMount", |
|
| 504 |
- "DmTaskRun", |
|
| 505 |
- "DmTaskSetTarget", |
|
| 506 |
- "DmTaskSetSector", |
|
| 507 |
- "DmTaskSetCookie", |
|
| 508 |
- "DmUdevWait", |
|
| 509 |
- "DmTaskSetName", |
|
| 510 |
- "DmTaskSetMessage", |
|
| 511 |
- "DmTaskSetAddNode", |
|
| 512 |
- ) |
|
| 513 |
- |
|
| 514 |
- }() |
|
| 515 |
- |
|
| 516 |
- runtime.GC() |
|
| 517 |
- |
|
| 518 |
- calls.Assert(t, |
|
| 519 |
- "DmTaskDestroy", |
|
| 520 |
- ) |
|
| 521 |
-} |
|
| 522 |
- |
|
| 523 |
-func TestDriverRemove(t *testing.T) {
|
|
| 524 |
- denyAllDevmapper() |
|
| 525 |
- denyAllSyscall() |
|
| 526 |
- defer denyAllSyscall() |
|
| 527 |
- defer denyAllDevmapper() |
|
| 528 |
- |
|
| 529 |
- calls := make(Set) |
|
| 530 |
- mockAllDevmapper(calls) |
|
| 531 |
- |
|
| 532 |
- sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
|
|
| 533 |
- calls["sysMount"] = true |
|
| 534 |
- // FIXME: compare the exact source and target strings (inodes + devname) |
|
| 535 |
- if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) {
|
|
| 536 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source)
|
|
| 537 |
- } |
|
| 538 |
- if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
|
|
| 539 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
|
|
| 540 |
- } |
|
| 541 |
- if expectedFstype := "ext4"; fstype != expectedFstype {
|
|
| 542 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype)
|
|
| 543 |
- } |
|
| 544 |
- if expectedFlags := uintptr(3236757504); flags != expectedFlags {
|
|
| 545 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
|
|
| 546 |
- } |
|
| 547 |
- return nil |
|
| 548 |
- } |
|
| 549 |
- sysUnmount = func(target string, flags int) (err error) {
|
|
| 550 |
- calls["sysUnmount"] = true |
|
| 551 |
- // FIXME: compare the exact source and target strings (inodes + devname) |
|
| 552 |
- if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
|
|
| 553 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
|
|
| 554 |
- } |
|
| 555 |
- if expectedFlags := 0; flags != expectedFlags {
|
|
| 556 |
- t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
|
|
| 557 |
- } |
|
| 558 |
- return nil |
|
| 559 |
- } |
|
| 560 |
- Mounted = func(mnt string) (bool, error) {
|
|
| 561 |
- calls["Mounted"] = true |
|
| 562 |
- return false, nil |
|
| 563 |
- } |
|
| 564 |
- |
|
| 565 |
- sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
|
| 566 |
- calls["sysSyscall"] = true |
|
| 567 |
- if trap != sysSysIoctl {
|
|
| 568 |
- t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap)
|
|
| 569 |
- } |
|
| 570 |
- switch a2 {
|
|
| 571 |
- case LoopSetFd: |
|
| 572 |
- calls["ioctl.loopsetfd"] = true |
|
| 573 |
- case LoopCtlGetFree: |
|
| 574 |
- calls["ioctl.loopctlgetfree"] = true |
|
| 575 |
- case LoopGetStatus64: |
|
| 576 |
- calls["ioctl.loopgetstatus"] = true |
|
| 577 |
- case LoopSetStatus64: |
|
| 578 |
- calls["ioctl.loopsetstatus"] = true |
|
| 579 |
- case LoopClrFd: |
|
| 580 |
- calls["ioctl.loopclrfd"] = true |
|
| 581 |
- case LoopSetCapacity: |
|
| 582 |
- calls["ioctl.loopsetcapacity"] = true |
|
| 583 |
- case BlkGetSize64: |
|
| 584 |
- calls["ioctl.blkgetsize"] = true |
|
| 585 |
- default: |
|
| 586 |
- t.Fatalf("Unexpected IOCTL. Received %d", a2)
|
|
| 587 |
- } |
|
| 588 |
- return 0, 0, 0 |
|
| 589 |
- } |
|
| 590 |
- |
|
| 591 |
- func() {
|
|
| 592 |
- d := newDriver(t) |
|
| 593 |
- |
|
| 594 |
- calls.Assert(t, |
|
| 595 |
- "DmSetDevDir", |
|
| 596 |
- "DmLogWithErrnoInit", |
|
| 597 |
- "DmTaskSetName", |
|
| 598 |
- "DmTaskRun", |
|
| 599 |
- "DmTaskGetInfo", |
|
| 600 |
- "execRun", |
|
| 601 |
- "DmTaskCreate", |
|
| 602 |
- "DmTaskSetTarget", |
|
| 603 |
- "DmTaskSetCookie", |
|
| 604 |
- "DmUdevWait", |
|
| 605 |
- "DmTaskSetSector", |
|
| 606 |
- "DmTaskSetMessage", |
|
| 607 |
- "DmTaskSetAddNode", |
|
| 608 |
- "sysSyscall", |
|
| 609 |
- "ioctl.blkgetsize", |
|
| 610 |
- "ioctl.loopsetfd", |
|
| 611 |
- "ioctl.loopsetstatus", |
|
| 612 |
- "?ioctl.loopctlgetfree", |
|
| 613 |
- ) |
|
| 614 |
- |
|
| 615 |
- if err := d.Create("1", ""); err != nil {
|
|
| 616 |
- t.Fatal(err) |
|
| 617 |
- } |
|
| 618 |
- |
|
| 619 |
- calls.Assert(t, |
|
| 620 |
- "DmTaskCreate", |
|
| 621 |
- "DmTaskGetInfo", |
|
| 622 |
- "sysMount", |
|
| 623 |
- "DmTaskRun", |
|
| 624 |
- "DmTaskSetTarget", |
|
| 625 |
- "DmTaskSetSector", |
|
| 626 |
- "DmTaskSetCookie", |
|
| 627 |
- "DmUdevWait", |
|
| 628 |
- "DmTaskSetName", |
|
| 629 |
- "DmTaskSetMessage", |
|
| 630 |
- "DmTaskSetAddNode", |
|
| 631 |
- ) |
|
| 632 |
- |
|
| 633 |
- Mounted = func(mnt string) (bool, error) {
|
|
| 634 |
- calls["Mounted"] = true |
|
| 635 |
- return true, nil |
|
| 636 |
- } |
|
| 637 |
- |
|
| 638 |
- if err := d.Remove("1"); err != nil {
|
|
| 639 |
- t.Fatal(err) |
|
| 640 |
- } |
|
| 641 |
- |
|
| 642 |
- calls.Assert(t, |
|
| 643 |
- "DmTaskRun", |
|
| 644 |
- "DmTaskSetSector", |
|
| 645 |
- "DmTaskSetName", |
|
| 646 |
- "DmTaskSetMessage", |
|
| 647 |
- "DmTaskCreate", |
|
| 648 |
- "DmTaskGetInfo", |
|
| 649 |
- "DmTaskSetCookie", |
|
| 650 |
- "DmTaskSetTarget", |
|
| 651 |
- "DmTaskSetAddNode", |
|
| 652 |
- "DmUdevWait", |
|
| 653 |
- "sysUnmount", |
|
| 654 |
- ) |
|
| 655 |
- }() |
|
| 656 |
- runtime.GC() |
|
| 657 |
- |
|
| 658 |
- calls.Assert(t, |
|
| 659 |
- "DmTaskDestroy", |
|
| 660 |
- ) |
|
| 661 |
-} |
|
| 662 |
- |
|
| 663 |
-func TestCleanup(t *testing.T) {
|
|
| 664 |
- t.Skip("FIXME: not a unit test")
|
|
| 665 |
- t.Skip("Unimplemented")
|
|
| 666 |
- d := newDriver(t) |
|
| 667 |
- defer osRemoveAll(d.home) |
|
| 668 |
- |
|
| 669 |
- mountPoints := make([]string, 2) |
|
| 670 |
- |
|
| 671 |
- if err := d.Create("1", ""); err != nil {
|
|
| 672 |
- t.Fatal(err) |
|
| 673 |
- } |
|
| 674 |
- // Mount the id |
|
| 675 |
- p, err := d.Get("1")
|
|
| 676 |
- if err != nil {
|
|
| 677 |
- t.Fatal(err) |
|
| 678 |
- } |
|
| 679 |
- mountPoints[0] = p |
|
| 680 |
- |
|
| 681 |
- if err := d.Create("2", "1"); err != nil {
|
|
| 682 |
- t.Fatal(err) |
|
| 683 |
- } |
|
| 684 |
- |
|
| 685 |
- p, err = d.Get("2")
|
|
| 686 |
- if err != nil {
|
|
| 687 |
- t.Fatal(err) |
|
| 688 |
- } |
|
| 689 |
- mountPoints[1] = p |
|
| 690 |
- |
|
| 691 |
- // Ensure that all the mount points are currently mounted |
|
| 692 |
- for _, p := range mountPoints {
|
|
| 693 |
- if mounted, err := Mounted(p); err != nil {
|
|
| 694 |
- t.Fatal(err) |
|
| 695 |
- } else if !mounted {
|
|
| 696 |
- t.Fatalf("Expected %s to be mounted", p)
|
|
| 697 |
- } |
|
| 698 |
- } |
|
| 699 |
- |
|
| 700 |
- // Ensure that devices are active |
|
| 701 |
- for _, p := range []string{"1", "2"} {
|
|
| 702 |
- if !d.HasActivatedDevice(p) {
|
|
| 703 |
- t.Fatalf("Expected %s to have an active device", p)
|
|
| 704 |
- } |
|
| 705 |
- } |
|
| 706 |
- |
|
| 707 |
- if err := d.Cleanup(); err != nil {
|
|
| 708 |
- t.Fatal(err) |
|
| 709 |
- } |
|
| 710 |
- |
|
| 711 |
- // Ensure that all the mount points are no longer mounted |
|
| 712 |
- for _, p := range mountPoints {
|
|
| 713 |
- if mounted, err := Mounted(p); err != nil {
|
|
| 714 |
- t.Fatal(err) |
|
| 715 |
- } else if mounted {
|
|
| 716 |
- t.Fatalf("Expected %s to not be mounted", p)
|
|
| 717 |
- } |
|
| 718 |
- } |
|
| 719 |
- |
|
| 720 |
- // Ensure that devices are no longer activated |
|
| 721 |
- for _, p := range []string{"1", "2"} {
|
|
| 722 |
- if d.HasActivatedDevice(p) {
|
|
| 723 |
- t.Fatalf("Expected %s not be an active device", p)
|
|
| 724 |
- } |
|
| 725 |
- } |
|
| 726 |
-} |
|
| 727 |
- |
|
| 728 |
-func TestNotMounted(t *testing.T) {
|
|
| 729 |
- t.Skip("FIXME: not a unit test")
|
|
| 730 |
- t.Skip("Not implemented")
|
|
| 731 |
- d := newDriver(t) |
|
| 732 |
- defer cleanup(d) |
|
| 733 |
- |
|
| 734 |
- if err := d.Create("1", ""); err != nil {
|
|
| 735 |
- t.Fatal(err) |
|
| 736 |
- } |
|
| 737 |
- |
|
| 738 |
- mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 739 |
- if err != nil {
|
|
| 740 |
- t.Fatal(err) |
|
| 741 |
- } |
|
| 742 |
- if mounted {
|
|
| 743 |
- t.Fatal("Id 1 should not be mounted")
|
|
| 744 |
- } |
|
| 745 |
-} |
|
| 746 |
- |
|
| 747 |
-func TestMounted(t *testing.T) {
|
|
| 748 |
- t.Skip("FIXME: not a unit test")
|
|
| 749 |
- d := newDriver(t) |
|
| 750 |
- defer cleanup(d) |
|
| 751 |
- |
|
| 752 |
- if err := d.Create("1", ""); err != nil {
|
|
| 753 |
- t.Fatal(err) |
|
| 754 |
- } |
|
| 755 |
- if _, err := d.Get("1"); err != nil {
|
|
| 756 |
- t.Fatal(err) |
|
| 757 |
- } |
|
| 758 |
- |
|
| 759 |
- mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 760 |
- if err != nil {
|
|
| 761 |
- t.Fatal(err) |
|
| 762 |
- } |
|
| 763 |
- if !mounted {
|
|
| 764 |
- t.Fatal("Id 1 should be mounted")
|
|
| 765 |
- } |
|
| 766 |
-} |
|
| 767 |
- |
|
| 768 |
-func TestInitCleanedDriver(t *testing.T) {
|
|
| 769 |
- t.Skip("FIXME: not a unit test")
|
|
| 770 |
- d := newDriver(t) |
|
| 771 |
- |
|
| 772 |
- if err := d.Create("1", ""); err != nil {
|
|
| 773 |
- t.Fatal(err) |
|
| 774 |
- } |
|
| 775 |
- if _, err := d.Get("1"); err != nil {
|
|
| 776 |
- t.Fatal(err) |
|
| 777 |
- } |
|
| 778 |
- |
|
| 779 |
- if err := d.Cleanup(); err != nil {
|
|
| 780 |
- t.Fatal(err) |
|
| 781 |
- } |
|
| 782 |
- |
|
| 783 |
- driver, err := Init(d.home) |
|
| 784 |
- if err != nil {
|
|
| 785 |
- t.Fatal(err) |
|
| 786 |
- } |
|
| 787 |
- d = driver.(*Driver) |
|
| 788 |
- defer cleanup(d) |
|
| 789 |
- |
|
| 790 |
- if _, err := d.Get("1"); err != nil {
|
|
| 791 |
- t.Fatal(err) |
|
| 792 |
- } |
|
| 793 |
-} |
|
| 794 |
- |
|
| 795 |
-func TestMountMountedDriver(t *testing.T) {
|
|
| 796 |
- t.Skip("FIXME: not a unit test")
|
|
| 797 |
- d := newDriver(t) |
|
| 798 |
- defer cleanup(d) |
|
| 799 |
- |
|
| 800 |
- if err := d.Create("1", ""); err != nil {
|
|
| 801 |
- t.Fatal(err) |
|
| 802 |
- } |
|
| 803 |
- |
|
| 804 |
- // Perform get on same id to ensure that it will |
|
| 805 |
- // not be mounted twice |
|
| 806 |
- if _, err := d.Get("1"); err != nil {
|
|
| 807 |
- t.Fatal(err) |
|
| 808 |
- } |
|
| 809 |
- if _, err := d.Get("1"); err != nil {
|
|
| 810 |
- t.Fatal(err) |
|
| 811 |
- } |
|
| 812 |
-} |
|
| 813 |
- |
|
| 814 |
-func TestGetReturnsValidDevice(t *testing.T) {
|
|
| 815 |
- t.Skip("FIXME: not a unit test")
|
|
| 816 |
- d := newDriver(t) |
|
| 817 |
- defer cleanup(d) |
|
| 818 |
- |
|
| 819 |
- if err := d.Create("1", ""); err != nil {
|
|
| 820 |
- t.Fatal(err) |
|
| 821 |
- } |
|
| 822 |
- |
|
| 823 |
- if !d.HasDevice("1") {
|
|
| 824 |
- t.Fatalf("Expected id 1 to be in device set")
|
|
| 825 |
- } |
|
| 826 |
- |
|
| 827 |
- if _, err := d.Get("1"); err != nil {
|
|
| 828 |
- t.Fatal(err) |
|
| 829 |
- } |
|
| 830 |
- |
|
| 831 |
- if !d.HasActivatedDevice("1") {
|
|
| 832 |
- t.Fatalf("Expected id 1 to be activated")
|
|
| 833 |
- } |
|
| 834 |
- |
|
| 835 |
- if !d.HasInitializedDevice("1") {
|
|
| 836 |
- t.Fatalf("Expected id 1 to be initialized")
|
|
| 837 |
- } |
|
| 838 |
-} |
|
| 839 |
- |
|
| 840 |
-func TestDriverGetSize(t *testing.T) {
|
|
| 841 |
- t.Skip("FIXME: not a unit test")
|
|
| 842 |
- t.Skipf("Size is currently not implemented")
|
|
| 843 |
- |
|
| 844 |
- d := newDriver(t) |
|
| 845 |
- defer cleanup(d) |
|
| 846 |
- |
|
| 847 |
- if err := d.Create("1", ""); err != nil {
|
|
| 848 |
- t.Fatal(err) |
|
| 849 |
- } |
|
| 850 |
- |
|
| 851 |
- mountPoint, err := d.Get("1")
|
|
| 852 |
- if err != nil {
|
|
| 853 |
- t.Fatal(err) |
|
| 854 |
- } |
|
| 855 |
- |
|
| 856 |
- size := int64(1024) |
|
| 857 |
- |
|
| 858 |
- f, err := osCreate(path.Join(mountPoint, "test_file")) |
|
| 859 |
- if err != nil {
|
|
| 860 |
- t.Fatal(err) |
|
| 861 |
- } |
|
| 862 |
- if err := f.Truncate(size); err != nil {
|
|
| 863 |
- t.Fatal(err) |
|
| 864 |
- } |
|
| 865 |
- f.Close() |
|
| 866 |
- |
|
| 867 |
- // diffSize, err := d.DiffSize("1")
|
|
| 868 |
- // if err != nil {
|
|
| 869 |
- // t.Fatal(err) |
|
| 870 |
- // } |
|
| 871 |
- // if diffSize != size {
|
|
| 872 |
- // t.Fatalf("Expected size %d got %d", size, diffSize)
|
|
| 873 |
- // } |
|
| 874 |
-} |
|
| 875 |
- |
|
| 876 |
-func assertMap(t *testing.T, m map[string]bool, keys ...string) {
|
|
| 877 |
- for _, key := range keys {
|
|
| 878 |
- if _, exists := m[key]; !exists {
|
|
| 879 |
- t.Fatalf("Key not set: %s", key)
|
|
| 880 |
- } |
|
| 881 |
- delete(m, key) |
|
| 882 |
- } |
|
| 883 |
- if len(m) != 0 {
|
|
| 884 |
- t.Fatalf("Unexpected keys: %v", m)
|
|
| 885 |
- } |
|
| 886 |
-} |
| 887 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,71 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "unsafe" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func ioctlLoopCtlGetFree(fd uintptr) (int, error) {
|
|
| 10 |
- index, _, err := sysSyscall(sysSysIoctl, fd, LoopCtlGetFree, 0) |
|
| 11 |
- if err != 0 {
|
|
| 12 |
- return 0, err |
|
| 13 |
- } |
|
| 14 |
- return int(index), nil |
|
| 15 |
-} |
|
| 16 |
- |
|
| 17 |
-func ioctlLoopSetFd(loopFd, sparseFd uintptr) error {
|
|
| 18 |
- if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetFd, sparseFd); err != 0 {
|
|
| 19 |
- return err |
|
| 20 |
- } |
|
| 21 |
- return nil |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *LoopInfo64) error {
|
|
| 25 |
- if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
|
|
| 26 |
- return err |
|
| 27 |
- } |
|
| 28 |
- return nil |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-func ioctlLoopClrFd(loopFd uintptr) error {
|
|
| 32 |
- if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopClrFd, 0); err != 0 {
|
|
| 33 |
- return err |
|
| 34 |
- } |
|
| 35 |
- return nil |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) {
|
|
| 39 |
- loopInfo := &LoopInfo64{}
|
|
| 40 |
- |
|
| 41 |
- if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
|
|
| 42 |
- return nil, err |
|
| 43 |
- } |
|
| 44 |
- return loopInfo, nil |
|
| 45 |
-} |
|
| 46 |
- |
|
| 47 |
-func ioctlLoopSetCapacity(loopFd uintptr, value int) error {
|
|
| 48 |
- if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetCapacity, uintptr(value)); err != 0 {
|
|
| 49 |
- return err |
|
| 50 |
- } |
|
| 51 |
- return nil |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-func ioctlBlkGetSize64(fd uintptr) (int64, error) {
|
|
| 55 |
- var size int64 |
|
| 56 |
- if _, _, err := sysSyscall(sysSysIoctl, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 {
|
|
| 57 |
- return 0, err |
|
| 58 |
- } |
|
| 59 |
- return size, nil |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-func ioctlBlkDiscard(fd uintptr, offset, length uint64) error {
|
|
| 63 |
- var r [2]uint64 |
|
| 64 |
- r[0] = offset |
|
| 65 |
- r[1] = length |
|
| 66 |
- |
|
| 67 |
- if _, _, err := sysSyscall(sysSysIoctl, fd, BlkDiscard, uintptr(unsafe.Pointer(&r[0]))); err != 0 {
|
|
| 68 |
- return err |
|
| 69 |
- } |
|
| 70 |
- return nil |
|
| 71 |
-} |
| 72 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,27 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "path/filepath" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-// FIXME: this is copy-pasted from the aufs driver. |
|
| 10 |
-// It should be moved into the core. |
|
| 11 |
- |
|
| 12 |
-var Mounted = func(mountpoint string) (bool, error) {
|
|
| 13 |
- mntpoint, err := osStat(mountpoint) |
|
| 14 |
- if err != nil {
|
|
| 15 |
- if osIsNotExist(err) {
|
|
| 16 |
- return false, nil |
|
| 17 |
- } |
|
| 18 |
- return false, err |
|
| 19 |
- } |
|
| 20 |
- parent, err := osStat(filepath.Join(mountpoint, "..")) |
|
| 21 |
- if err != nil {
|
|
| 22 |
- return false, err |
|
| 23 |
- } |
|
| 24 |
- mntpointSt := toSysStatT(mntpoint.Sys()) |
|
| 25 |
- parentSt := toSysStatT(parent.Sys()) |
|
| 26 |
- return mntpointSt.Dev != parentSt.Dev, nil |
|
| 27 |
-} |
| 28 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,57 +0,0 @@ |
| 1 |
-// +build linux,amd64 |
|
| 2 |
- |
|
| 3 |
-package devmapper |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "os" |
|
| 7 |
- "os/exec" |
|
| 8 |
- "syscall" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-type ( |
|
| 12 |
- sysStatT syscall.Stat_t |
|
| 13 |
- sysErrno syscall.Errno |
|
| 14 |
- |
|
| 15 |
- osFile struct{ *os.File }
|
|
| 16 |
-) |
|
| 17 |
- |
|
| 18 |
-var ( |
|
| 19 |
- sysMount = syscall.Mount |
|
| 20 |
- sysUnmount = syscall.Unmount |
|
| 21 |
- sysCloseOnExec = syscall.CloseOnExec |
|
| 22 |
- sysSyscall = syscall.Syscall |
|
| 23 |
- |
|
| 24 |
- osOpenFile = func(name string, flag int, perm os.FileMode) (*osFile, error) {
|
|
| 25 |
- f, err := os.OpenFile(name, flag, perm) |
|
| 26 |
- return &osFile{File: f}, err
|
|
| 27 |
- } |
|
| 28 |
- osOpen = func(name string) (*osFile, error) { f, err := os.Open(name); return &osFile{File: f}, err }
|
|
| 29 |
- osNewFile = os.NewFile |
|
| 30 |
- osCreate = os.Create |
|
| 31 |
- osStat = os.Stat |
|
| 32 |
- osIsNotExist = os.IsNotExist |
|
| 33 |
- osIsExist = os.IsExist |
|
| 34 |
- osMkdirAll = os.MkdirAll |
|
| 35 |
- osRemoveAll = os.RemoveAll |
|
| 36 |
- osRename = os.Rename |
|
| 37 |
- osReadlink = os.Readlink |
|
| 38 |
- |
|
| 39 |
- execRun = func(name string, args ...string) error { return exec.Command(name, args...).Run() }
|
|
| 40 |
-) |
|
| 41 |
- |
|
| 42 |
-const ( |
|
| 43 |
- sysMsMgcVal = syscall.MS_MGC_VAL |
|
| 44 |
- sysMsRdOnly = syscall.MS_RDONLY |
|
| 45 |
- sysEInval = syscall.EINVAL |
|
| 46 |
- sysSysIoctl = syscall.SYS_IOCTL |
|
| 47 |
- sysEBusy = syscall.EBUSY |
|
| 48 |
- |
|
| 49 |
- osORdOnly = os.O_RDONLY |
|
| 50 |
- osORdWr = os.O_RDWR |
|
| 51 |
- osOCreate = os.O_CREATE |
|
| 52 |
- osModeDevice = os.ModeDevice |
|
| 53 |
-) |
|
| 54 |
- |
|
| 55 |
-func toSysStatT(i interface{}) *sysStatT {
|
|
| 56 |
- return (*sysStatT)(i.(*syscall.Stat_t)) |
|
| 57 |
-} |
| 58 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,93 +0,0 @@ |
| 1 |
-package graphdriver |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/archive" |
|
| 6 |
- "github.com/dotcloud/docker/utils" |
|
| 7 |
- "os" |
|
| 8 |
- "path" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-type InitFunc func(root string) (Driver, error) |
|
| 12 |
- |
|
| 13 |
-type Driver interface {
|
|
| 14 |
- String() string |
|
| 15 |
- |
|
| 16 |
- Create(id, parent string) error |
|
| 17 |
- Remove(id string) error |
|
| 18 |
- |
|
| 19 |
- Get(id string) (dir string, err error) |
|
| 20 |
- Put(id string) |
|
| 21 |
- Exists(id string) bool |
|
| 22 |
- |
|
| 23 |
- Status() [][2]string |
|
| 24 |
- |
|
| 25 |
- Cleanup() error |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-type Differ interface {
|
|
| 29 |
- Diff(id string) (archive.Archive, error) |
|
| 30 |
- Changes(id string) ([]archive.Change, error) |
|
| 31 |
- ApplyDiff(id string, diff archive.ArchiveReader) error |
|
| 32 |
- DiffSize(id string) (bytes int64, err error) |
|
| 33 |
-} |
|
| 34 |
- |
|
| 35 |
-var ( |
|
| 36 |
- DefaultDriver string |
|
| 37 |
- // All registred drivers |
|
| 38 |
- drivers map[string]InitFunc |
|
| 39 |
- // Slice of drivers that should be used in an order |
|
| 40 |
- priority = []string{
|
|
| 41 |
- "aufs", |
|
| 42 |
- "devicemapper", |
|
| 43 |
- "vfs", |
|
| 44 |
- // experimental, has to be enabled manually for now |
|
| 45 |
- "btrfs", |
|
| 46 |
- } |
|
| 47 |
-) |
|
| 48 |
- |
|
| 49 |
-func init() {
|
|
| 50 |
- drivers = make(map[string]InitFunc) |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-func Register(name string, initFunc InitFunc) error {
|
|
| 54 |
- if _, exists := drivers[name]; exists {
|
|
| 55 |
- return fmt.Errorf("Name already registered %s", name)
|
|
| 56 |
- } |
|
| 57 |
- drivers[name] = initFunc |
|
| 58 |
- |
|
| 59 |
- return nil |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-func GetDriver(name, home string) (Driver, error) {
|
|
| 63 |
- if initFunc, exists := drivers[name]; exists {
|
|
| 64 |
- return initFunc(path.Join(home, name)) |
|
| 65 |
- } |
|
| 66 |
- return nil, fmt.Errorf("No such driver: %s", name)
|
|
| 67 |
-} |
|
| 68 |
- |
|
| 69 |
-func New(root string) (driver Driver, err error) {
|
|
| 70 |
- for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
|
|
| 71 |
- if name != "" {
|
|
| 72 |
- return GetDriver(name, root) |
|
| 73 |
- } |
|
| 74 |
- } |
|
| 75 |
- |
|
| 76 |
- // Check for priority drivers first |
|
| 77 |
- for _, name := range priority {
|
|
| 78 |
- if driver, err = GetDriver(name, root); err != nil {
|
|
| 79 |
- utils.Debugf("Error loading driver %s: %s", name, err)
|
|
| 80 |
- continue |
|
| 81 |
- } |
|
| 82 |
- return driver, nil |
|
| 83 |
- } |
|
| 84 |
- |
|
| 85 |
- // Check all registered drivers if no priority driver is found |
|
| 86 |
- for _, initFunc := range drivers {
|
|
| 87 |
- if driver, err = initFunc(root); err != nil {
|
|
| 88 |
- continue |
|
| 89 |
- } |
|
| 90 |
- return driver, nil |
|
| 91 |
- } |
|
| 92 |
- return nil, err |
|
| 93 |
-} |
| 94 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,95 +0,0 @@ |
| 1 |
-package vfs |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 6 |
- "os" |
|
| 7 |
- "os/exec" |
|
| 8 |
- "path" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-func init() {
|
|
| 12 |
- graphdriver.Register("vfs", Init)
|
|
| 13 |
-} |
|
| 14 |
- |
|
| 15 |
-func Init(home string) (graphdriver.Driver, error) {
|
|
| 16 |
- d := &Driver{
|
|
| 17 |
- home: home, |
|
| 18 |
- } |
|
| 19 |
- return d, nil |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-type Driver struct {
|
|
| 23 |
- home string |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-func (d *Driver) String() string {
|
|
| 27 |
- return "vfs" |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 |
-func (d *Driver) Status() [][2]string {
|
|
| 31 |
- return nil |
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-func (d *Driver) Cleanup() error {
|
|
| 35 |
- return nil |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func copyDir(src, dst string) error {
|
|
| 39 |
- if output, err := exec.Command("cp", "-aT", "--reflink=auto", src, dst).CombinedOutput(); err != nil {
|
|
| 40 |
- return fmt.Errorf("Error VFS copying directory: %s (%s)", err, output)
|
|
| 41 |
- } |
|
| 42 |
- return nil |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-func (d *Driver) Create(id string, parent string) error {
|
|
| 46 |
- dir := d.dir(id) |
|
| 47 |
- if err := os.MkdirAll(path.Dir(dir), 0700); err != nil {
|
|
| 48 |
- return err |
|
| 49 |
- } |
|
| 50 |
- if err := os.Mkdir(dir, 0700); err != nil {
|
|
| 51 |
- return err |
|
| 52 |
- } |
|
| 53 |
- if parent == "" {
|
|
| 54 |
- return nil |
|
| 55 |
- } |
|
| 56 |
- parentDir, err := d.Get(parent) |
|
| 57 |
- if err != nil {
|
|
| 58 |
- return fmt.Errorf("%s: %s", parent, err)
|
|
| 59 |
- } |
|
| 60 |
- if err := copyDir(parentDir, dir); err != nil {
|
|
| 61 |
- return err |
|
| 62 |
- } |
|
| 63 |
- return nil |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-func (d *Driver) dir(id string) string {
|
|
| 67 |
- return path.Join(d.home, "dir", path.Base(id)) |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-func (d *Driver) Remove(id string) error {
|
|
| 71 |
- if _, err := os.Stat(d.dir(id)); err != nil {
|
|
| 72 |
- return err |
|
| 73 |
- } |
|
| 74 |
- return os.RemoveAll(d.dir(id)) |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-func (d *Driver) Get(id string) (string, error) {
|
|
| 78 |
- dir := d.dir(id) |
|
| 79 |
- if st, err := os.Stat(dir); err != nil {
|
|
| 80 |
- return "", err |
|
| 81 |
- } else if !st.IsDir() {
|
|
| 82 |
- return "", fmt.Errorf("%s: not a directory", dir)
|
|
| 83 |
- } |
|
| 84 |
- return dir, nil |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-func (d *Driver) Put(id string) {
|
|
| 88 |
- // The vfs driver has no runtime resources (e.g. mounts) |
|
| 89 |
- // to clean up, so we don't need anything here |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-func (d *Driver) Exists(id string) bool {
|
|
| 93 |
- _, err := os.Stat(d.dir(id)) |
|
| 94 |
- return err == nil |
|
| 95 |
-} |
| ... | ... |
@@ -4,7 +4,7 @@ import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"fmt" |
| 6 | 6 |
"github.com/dotcloud/docker/archive" |
| 7 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 7 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 8 | 8 |
"github.com/dotcloud/docker/runconfig" |
| 9 | 9 |
"github.com/dotcloud/docker/utils" |
| 10 | 10 |
"io/ioutil" |
| ... | ... |
@@ -5,7 +5,7 @@ import ( |
| 5 | 5 |
"github.com/dotcloud/docker/archive" |
| 6 | 6 |
"github.com/dotcloud/docker/dockerversion" |
| 7 | 7 |
"github.com/dotcloud/docker/graph" |
| 8 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 8 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 9 | 9 |
"github.com/dotcloud/docker/image" |
| 10 | 10 |
"github.com/dotcloud/docker/utils" |
| 11 | 11 |
"io" |
| ... | ... |
@@ -7,7 +7,7 @@ import ( |
| 7 | 7 |
"github.com/dotcloud/docker/archive" |
| 8 | 8 |
"github.com/dotcloud/docker/engine" |
| 9 | 9 |
"github.com/dotcloud/docker/runtime/execdriver" |
| 10 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 10 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 11 | 11 |
"github.com/dotcloud/docker/image" |
| 12 | 12 |
"github.com/dotcloud/docker/links" |
| 13 | 13 |
"github.com/dotcloud/docker/nat" |
| 14 | 14 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,401 @@ |
| 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/runtime/graphdriver" |
|
| 27 |
+ mountpk "github.com/dotcloud/docker/pkg/mount" |
|
| 28 |
+ "github.com/dotcloud/docker/utils" |
|
| 29 |
+ "os" |
|
| 30 |
+ "os/exec" |
|
| 31 |
+ "path" |
|
| 32 |
+ "strings" |
|
| 33 |
+ "sync" |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+var ( |
|
| 37 |
+ ErrAufsNotSupported = fmt.Errorf("AUFS was not found in /proc/filesystems")
|
|
| 38 |
+) |
|
| 39 |
+ |
|
| 40 |
+func init() {
|
|
| 41 |
+ graphdriver.Register("aufs", Init)
|
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+type Driver struct {
|
|
| 45 |
+ root string |
|
| 46 |
+ sync.Mutex // Protects concurrent modification to active |
|
| 47 |
+ active map[string]int |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// New returns a new AUFS driver. |
|
| 51 |
+// An error is returned if AUFS is not supported. |
|
| 52 |
+func Init(root string) (graphdriver.Driver, error) {
|
|
| 53 |
+ // Try to load the aufs kernel module |
|
| 54 |
+ if err := supportsAufs(); err != nil {
|
|
| 55 |
+ return nil, err |
|
| 56 |
+ } |
|
| 57 |
+ paths := []string{
|
|
| 58 |
+ "mnt", |
|
| 59 |
+ "diff", |
|
| 60 |
+ "layers", |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ a := &Driver{
|
|
| 64 |
+ root: root, |
|
| 65 |
+ active: make(map[string]int), |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ // Create the root aufs driver dir and return |
|
| 69 |
+ // if it already exists |
|
| 70 |
+ // If not populate the dir structure |
|
| 71 |
+ if err := os.MkdirAll(root, 0755); err != nil {
|
|
| 72 |
+ if os.IsExist(err) {
|
|
| 73 |
+ return a, nil |
|
| 74 |
+ } |
|
| 75 |
+ return nil, err |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ for _, p := range paths {
|
|
| 79 |
+ if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
|
|
| 80 |
+ return nil, err |
|
| 81 |
+ } |
|
| 82 |
+ } |
|
| 83 |
+ return a, nil |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+// Return a nil error if the kernel supports aufs |
|
| 87 |
+// We cannot modprobe because inside dind modprobe fails |
|
| 88 |
+// to run |
|
| 89 |
+func supportsAufs() error {
|
|
| 90 |
+ // We can try to modprobe aufs first before looking at |
|
| 91 |
+ // proc/filesystems for when aufs is supported |
|
| 92 |
+ exec.Command("modprobe", "aufs").Run()
|
|
| 93 |
+ |
|
| 94 |
+ f, err := os.Open("/proc/filesystems")
|
|
| 95 |
+ if err != nil {
|
|
| 96 |
+ return err |
|
| 97 |
+ } |
|
| 98 |
+ defer f.Close() |
|
| 99 |
+ |
|
| 100 |
+ s := bufio.NewScanner(f) |
|
| 101 |
+ for s.Scan() {
|
|
| 102 |
+ if strings.Contains(s.Text(), "aufs") {
|
|
| 103 |
+ return nil |
|
| 104 |
+ } |
|
| 105 |
+ } |
|
| 106 |
+ return ErrAufsNotSupported |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+func (a Driver) rootPath() string {
|
|
| 110 |
+ return a.root |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+func (Driver) String() string {
|
|
| 114 |
+ return "aufs" |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+func (a Driver) Status() [][2]string {
|
|
| 118 |
+ ids, _ := loadIds(path.Join(a.rootPath(), "layers")) |
|
| 119 |
+ return [][2]string{
|
|
| 120 |
+ {"Root Dir", a.rootPath()},
|
|
| 121 |
+ {"Dirs", fmt.Sprintf("%d", len(ids))},
|
|
| 122 |
+ } |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+// Exists returns true if the given id is registered with |
|
| 126 |
+// this driver |
|
| 127 |
+func (a Driver) Exists(id string) bool {
|
|
| 128 |
+ if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
|
|
| 129 |
+ return false |
|
| 130 |
+ } |
|
| 131 |
+ return true |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+// Three folders are created for each id |
|
| 135 |
+// mnt, layers, and diff |
|
| 136 |
+func (a *Driver) Create(id, parent string) error {
|
|
| 137 |
+ if err := a.createDirsFor(id); err != nil {
|
|
| 138 |
+ return err |
|
| 139 |
+ } |
|
| 140 |
+ // Write the layers metadata |
|
| 141 |
+ f, err := os.Create(path.Join(a.rootPath(), "layers", id)) |
|
| 142 |
+ if err != nil {
|
|
| 143 |
+ return err |
|
| 144 |
+ } |
|
| 145 |
+ defer f.Close() |
|
| 146 |
+ |
|
| 147 |
+ if parent != "" {
|
|
| 148 |
+ ids, err := getParentIds(a.rootPath(), parent) |
|
| 149 |
+ if err != nil {
|
|
| 150 |
+ return err |
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ if _, err := fmt.Fprintln(f, parent); err != nil {
|
|
| 154 |
+ return err |
|
| 155 |
+ } |
|
| 156 |
+ for _, i := range ids {
|
|
| 157 |
+ if _, err := fmt.Fprintln(f, i); err != nil {
|
|
| 158 |
+ return err |
|
| 159 |
+ } |
|
| 160 |
+ } |
|
| 161 |
+ } |
|
| 162 |
+ return nil |
|
| 163 |
+} |
|
| 164 |
+ |
|
| 165 |
+func (a *Driver) createDirsFor(id string) error {
|
|
| 166 |
+ paths := []string{
|
|
| 167 |
+ "mnt", |
|
| 168 |
+ "diff", |
|
| 169 |
+ } |
|
| 170 |
+ |
|
| 171 |
+ for _, p := range paths {
|
|
| 172 |
+ if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
|
|
| 173 |
+ return err |
|
| 174 |
+ } |
|
| 175 |
+ } |
|
| 176 |
+ return nil |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+// Unmount and remove the dir information |
|
| 180 |
+func (a *Driver) Remove(id string) error {
|
|
| 181 |
+ // Protect the a.active from concurrent access |
|
| 182 |
+ a.Lock() |
|
| 183 |
+ defer a.Unlock() |
|
| 184 |
+ |
|
| 185 |
+ if a.active[id] != 0 {
|
|
| 186 |
+ utils.Errorf("Warning: removing active id %s\n", id)
|
|
| 187 |
+ } |
|
| 188 |
+ |
|
| 189 |
+ // Make sure the dir is umounted first |
|
| 190 |
+ if err := a.unmount(id); err != nil {
|
|
| 191 |
+ return err |
|
| 192 |
+ } |
|
| 193 |
+ tmpDirs := []string{
|
|
| 194 |
+ "mnt", |
|
| 195 |
+ "diff", |
|
| 196 |
+ } |
|
| 197 |
+ |
|
| 198 |
+ // Atomically remove each directory in turn by first moving it out of the |
|
| 199 |
+ // way (so that docker doesn't find it anymore) before doing removal of |
|
| 200 |
+ // the whole tree. |
|
| 201 |
+ for _, p := range tmpDirs {
|
|
| 202 |
+ |
|
| 203 |
+ realPath := path.Join(a.rootPath(), p, id) |
|
| 204 |
+ tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id))
|
|
| 205 |
+ if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) {
|
|
| 206 |
+ return err |
|
| 207 |
+ } |
|
| 208 |
+ defer os.RemoveAll(tmpPath) |
|
| 209 |
+ } |
|
| 210 |
+ |
|
| 211 |
+ // Remove the layers file for the id |
|
| 212 |
+ if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
|
|
| 213 |
+ return err |
|
| 214 |
+ } |
|
| 215 |
+ return nil |
|
| 216 |
+} |
|
| 217 |
+ |
|
| 218 |
+// Return the rootfs path for the id |
|
| 219 |
+// This will mount the dir at it's given path |
|
| 220 |
+func (a *Driver) Get(id string) (string, error) {
|
|
| 221 |
+ ids, err := getParentIds(a.rootPath(), id) |
|
| 222 |
+ if err != nil {
|
|
| 223 |
+ if !os.IsNotExist(err) {
|
|
| 224 |
+ return "", err |
|
| 225 |
+ } |
|
| 226 |
+ ids = []string{}
|
|
| 227 |
+ } |
|
| 228 |
+ |
|
| 229 |
+ // Protect the a.active from concurrent access |
|
| 230 |
+ a.Lock() |
|
| 231 |
+ defer a.Unlock() |
|
| 232 |
+ |
|
| 233 |
+ count := a.active[id] |
|
| 234 |
+ |
|
| 235 |
+ // If a dir does not have a parent ( no layers )do not try to mount |
|
| 236 |
+ // just return the diff path to the data |
|
| 237 |
+ out := path.Join(a.rootPath(), "diff", id) |
|
| 238 |
+ if len(ids) > 0 {
|
|
| 239 |
+ out = path.Join(a.rootPath(), "mnt", id) |
|
| 240 |
+ |
|
| 241 |
+ if count == 0 {
|
|
| 242 |
+ if err := a.mount(id); err != nil {
|
|
| 243 |
+ return "", err |
|
| 244 |
+ } |
|
| 245 |
+ } |
|
| 246 |
+ } |
|
| 247 |
+ |
|
| 248 |
+ a.active[id] = count + 1 |
|
| 249 |
+ |
|
| 250 |
+ return out, nil |
|
| 251 |
+} |
|
| 252 |
+ |
|
| 253 |
+func (a *Driver) Put(id string) {
|
|
| 254 |
+ // Protect the a.active from concurrent access |
|
| 255 |
+ a.Lock() |
|
| 256 |
+ defer a.Unlock() |
|
| 257 |
+ |
|
| 258 |
+ if count := a.active[id]; count > 1 {
|
|
| 259 |
+ a.active[id] = count - 1 |
|
| 260 |
+ } else {
|
|
| 261 |
+ ids, _ := getParentIds(a.rootPath(), id) |
|
| 262 |
+ // We only mounted if there are any parents |
|
| 263 |
+ if ids != nil && len(ids) > 0 {
|
|
| 264 |
+ a.unmount(id) |
|
| 265 |
+ } |
|
| 266 |
+ delete(a.active, id) |
|
| 267 |
+ } |
|
| 268 |
+} |
|
| 269 |
+ |
|
| 270 |
+// Returns an archive of the contents for the id |
|
| 271 |
+func (a *Driver) Diff(id string) (archive.Archive, error) {
|
|
| 272 |
+ return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
|
| 273 |
+ Compression: archive.Uncompressed, |
|
| 274 |
+ }) |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+func (a *Driver) ApplyDiff(id string, diff archive.ArchiveReader) error {
|
|
| 278 |
+ return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil) |
|
| 279 |
+} |
|
| 280 |
+ |
|
| 281 |
+// Returns the size of the contents for the id |
|
| 282 |
+func (a *Driver) DiffSize(id string) (int64, error) {
|
|
| 283 |
+ return utils.TreeSize(path.Join(a.rootPath(), "diff", id)) |
|
| 284 |
+} |
|
| 285 |
+ |
|
| 286 |
+func (a *Driver) Changes(id string) ([]archive.Change, error) {
|
|
| 287 |
+ layers, err := a.getParentLayerPaths(id) |
|
| 288 |
+ if err != nil {
|
|
| 289 |
+ return nil, err |
|
| 290 |
+ } |
|
| 291 |
+ return archive.Changes(layers, path.Join(a.rootPath(), "diff", id)) |
|
| 292 |
+} |
|
| 293 |
+ |
|
| 294 |
+func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
|
|
| 295 |
+ parentIds, err := getParentIds(a.rootPath(), id) |
|
| 296 |
+ if err != nil {
|
|
| 297 |
+ return nil, err |
|
| 298 |
+ } |
|
| 299 |
+ if len(parentIds) == 0 {
|
|
| 300 |
+ return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
|
|
| 301 |
+ } |
|
| 302 |
+ layers := make([]string, len(parentIds)) |
|
| 303 |
+ |
|
| 304 |
+ // Get the diff paths for all the parent ids |
|
| 305 |
+ for i, p := range parentIds {
|
|
| 306 |
+ layers[i] = path.Join(a.rootPath(), "diff", p) |
|
| 307 |
+ } |
|
| 308 |
+ return layers, nil |
|
| 309 |
+} |
|
| 310 |
+ |
|
| 311 |
+func (a *Driver) mount(id string) error {
|
|
| 312 |
+ // If the id is mounted or we get an error return |
|
| 313 |
+ if mounted, err := a.mounted(id); err != nil || mounted {
|
|
| 314 |
+ return err |
|
| 315 |
+ } |
|
| 316 |
+ |
|
| 317 |
+ var ( |
|
| 318 |
+ target = path.Join(a.rootPath(), "mnt", id) |
|
| 319 |
+ rw = path.Join(a.rootPath(), "diff", id) |
|
| 320 |
+ ) |
|
| 321 |
+ |
|
| 322 |
+ layers, err := a.getParentLayerPaths(id) |
|
| 323 |
+ if err != nil {
|
|
| 324 |
+ return err |
|
| 325 |
+ } |
|
| 326 |
+ |
|
| 327 |
+ if err := a.aufsMount(layers, rw, target); err != nil {
|
|
| 328 |
+ return err |
|
| 329 |
+ } |
|
| 330 |
+ return nil |
|
| 331 |
+} |
|
| 332 |
+ |
|
| 333 |
+func (a *Driver) unmount(id string) error {
|
|
| 334 |
+ if mounted, err := a.mounted(id); err != nil || !mounted {
|
|
| 335 |
+ return err |
|
| 336 |
+ } |
|
| 337 |
+ target := path.Join(a.rootPath(), "mnt", id) |
|
| 338 |
+ return Unmount(target) |
|
| 339 |
+} |
|
| 340 |
+ |
|
| 341 |
+func (a *Driver) mounted(id string) (bool, error) {
|
|
| 342 |
+ target := path.Join(a.rootPath(), "mnt", id) |
|
| 343 |
+ return mountpk.Mounted(target) |
|
| 344 |
+} |
|
| 345 |
+ |
|
| 346 |
+// During cleanup aufs needs to unmount all mountpoints |
|
| 347 |
+func (a *Driver) Cleanup() error {
|
|
| 348 |
+ ids, err := loadIds(path.Join(a.rootPath(), "layers")) |
|
| 349 |
+ if err != nil {
|
|
| 350 |
+ return err |
|
| 351 |
+ } |
|
| 352 |
+ for _, id := range ids {
|
|
| 353 |
+ if err := a.unmount(id); err != nil {
|
|
| 354 |
+ utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
|
|
| 355 |
+ } |
|
| 356 |
+ } |
|
| 357 |
+ return nil |
|
| 358 |
+} |
|
| 359 |
+ |
|
| 360 |
+func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
|
|
| 361 |
+ defer func() {
|
|
| 362 |
+ if err != nil {
|
|
| 363 |
+ Unmount(target) |
|
| 364 |
+ } |
|
| 365 |
+ }() |
|
| 366 |
+ |
|
| 367 |
+ if err = a.tryMount(ro, rw, target); err != nil {
|
|
| 368 |
+ if err = a.mountRw(rw, target); err != nil {
|
|
| 369 |
+ return |
|
| 370 |
+ } |
|
| 371 |
+ |
|
| 372 |
+ for _, layer := range ro {
|
|
| 373 |
+ branch := fmt.Sprintf("append:%s=ro+wh", layer)
|
|
| 374 |
+ if err = mount("none", target, "aufs", MsRemount, branch); err != nil {
|
|
| 375 |
+ return |
|
| 376 |
+ } |
|
| 377 |
+ } |
|
| 378 |
+ } |
|
| 379 |
+ return |
|
| 380 |
+} |
|
| 381 |
+ |
|
| 382 |
+// Try to mount using the aufs fast path, if this fails then |
|
| 383 |
+// append ro layers. |
|
| 384 |
+func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
|
|
| 385 |
+ var ( |
|
| 386 |
+ rwBranch = fmt.Sprintf("%s=rw", rw)
|
|
| 387 |
+ roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
|
|
| 388 |
+ ) |
|
| 389 |
+ return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
|
|
| 390 |
+} |
|
| 391 |
+ |
|
| 392 |
+func (a *Driver) mountRw(rw, target string) error {
|
|
| 393 |
+ return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
|
|
| 394 |
+} |
|
| 395 |
+ |
|
| 396 |
+func rollbackMount(target string, err error) {
|
|
| 397 |
+ if err != nil {
|
|
| 398 |
+ Unmount(target) |
|
| 399 |
+ } |
|
| 400 |
+} |
| 0 | 401 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,697 @@ |
| 0 |
+package aufs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "crypto/sha256" |
|
| 4 |
+ "encoding/hex" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/archive" |
|
| 7 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 8 |
+ "io/ioutil" |
|
| 9 |
+ "os" |
|
| 10 |
+ "path" |
|
| 11 |
+ "testing" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+var ( |
|
| 15 |
+ tmp = path.Join(os.TempDir(), "aufs-tests", "aufs") |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+func testInit(dir string, t *testing.T) graphdriver.Driver {
|
|
| 19 |
+ d, err := Init(dir) |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ if err == ErrAufsNotSupported {
|
|
| 22 |
+ t.Skip(err) |
|
| 23 |
+ } else {
|
|
| 24 |
+ t.Fatal(err) |
|
| 25 |
+ } |
|
| 26 |
+ } |
|
| 27 |
+ return d |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func newDriver(t *testing.T) *Driver {
|
|
| 31 |
+ if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 32 |
+ t.Fatal(err) |
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 35 |
+ d := testInit(tmp, t) |
|
| 36 |
+ return d.(*Driver) |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func TestNewDriver(t *testing.T) {
|
|
| 40 |
+ if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 41 |
+ t.Fatal(err) |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ d := testInit(tmp, t) |
|
| 45 |
+ defer os.RemoveAll(tmp) |
|
| 46 |
+ if d == nil {
|
|
| 47 |
+ t.Fatalf("Driver should not be nil")
|
|
| 48 |
+ } |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func TestAufsString(t *testing.T) {
|
|
| 52 |
+ d := newDriver(t) |
|
| 53 |
+ defer os.RemoveAll(tmp) |
|
| 54 |
+ |
|
| 55 |
+ if d.String() != "aufs" {
|
|
| 56 |
+ t.Fatalf("Expected aufs got %s", d.String())
|
|
| 57 |
+ } |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func TestCreateDirStructure(t *testing.T) {
|
|
| 61 |
+ newDriver(t) |
|
| 62 |
+ defer os.RemoveAll(tmp) |
|
| 63 |
+ |
|
| 64 |
+ paths := []string{
|
|
| 65 |
+ "mnt", |
|
| 66 |
+ "layers", |
|
| 67 |
+ "diff", |
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ for _, p := range paths {
|
|
| 71 |
+ if _, err := os.Stat(path.Join(tmp, p)); err != nil {
|
|
| 72 |
+ t.Fatal(err) |
|
| 73 |
+ } |
|
| 74 |
+ } |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+// We should be able to create two drivers with the same dir structure |
|
| 78 |
+func TestNewDriverFromExistingDir(t *testing.T) {
|
|
| 79 |
+ if err := os.MkdirAll(tmp, 0755); err != nil {
|
|
| 80 |
+ t.Fatal(err) |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ testInit(tmp, t) |
|
| 84 |
+ testInit(tmp, t) |
|
| 85 |
+ os.RemoveAll(tmp) |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+func TestCreateNewDir(t *testing.T) {
|
|
| 89 |
+ d := newDriver(t) |
|
| 90 |
+ defer os.RemoveAll(tmp) |
|
| 91 |
+ |
|
| 92 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 93 |
+ t.Fatal(err) |
|
| 94 |
+ } |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func TestCreateNewDirStructure(t *testing.T) {
|
|
| 98 |
+ d := newDriver(t) |
|
| 99 |
+ defer os.RemoveAll(tmp) |
|
| 100 |
+ |
|
| 101 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 102 |
+ t.Fatal(err) |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ paths := []string{
|
|
| 106 |
+ "mnt", |
|
| 107 |
+ "diff", |
|
| 108 |
+ "layers", |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ for _, p := range paths {
|
|
| 112 |
+ if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
|
|
| 113 |
+ t.Fatal(err) |
|
| 114 |
+ } |
|
| 115 |
+ } |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+func TestRemoveImage(t *testing.T) {
|
|
| 119 |
+ d := newDriver(t) |
|
| 120 |
+ defer os.RemoveAll(tmp) |
|
| 121 |
+ |
|
| 122 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 123 |
+ t.Fatal(err) |
|
| 124 |
+ } |
|
| 125 |
+ |
|
| 126 |
+ if err := d.Remove("1"); err != nil {
|
|
| 127 |
+ t.Fatal(err) |
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ paths := []string{
|
|
| 131 |
+ "mnt", |
|
| 132 |
+ "diff", |
|
| 133 |
+ "layers", |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ for _, p := range paths {
|
|
| 137 |
+ if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
|
|
| 138 |
+ t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
|
|
| 139 |
+ } |
|
| 140 |
+ } |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func TestGetWithoutParent(t *testing.T) {
|
|
| 144 |
+ d := newDriver(t) |
|
| 145 |
+ defer os.RemoveAll(tmp) |
|
| 146 |
+ |
|
| 147 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 148 |
+ t.Fatal(err) |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ diffPath, err := d.Get("1")
|
|
| 152 |
+ if err != nil {
|
|
| 153 |
+ t.Fatal(err) |
|
| 154 |
+ } |
|
| 155 |
+ expected := path.Join(tmp, "diff", "1") |
|
| 156 |
+ if diffPath != expected {
|
|
| 157 |
+ t.Fatalf("Expected path %s got %s", expected, diffPath)
|
|
| 158 |
+ } |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+func TestCleanupWithNoDirs(t *testing.T) {
|
|
| 162 |
+ d := newDriver(t) |
|
| 163 |
+ defer os.RemoveAll(tmp) |
|
| 164 |
+ |
|
| 165 |
+ if err := d.Cleanup(); err != nil {
|
|
| 166 |
+ t.Fatal(err) |
|
| 167 |
+ } |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+func TestCleanupWithDir(t *testing.T) {
|
|
| 171 |
+ d := newDriver(t) |
|
| 172 |
+ defer os.RemoveAll(tmp) |
|
| 173 |
+ |
|
| 174 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 175 |
+ t.Fatal(err) |
|
| 176 |
+ } |
|
| 177 |
+ |
|
| 178 |
+ if err := d.Cleanup(); err != nil {
|
|
| 179 |
+ t.Fatal(err) |
|
| 180 |
+ } |
|
| 181 |
+} |
|
| 182 |
+ |
|
| 183 |
+func TestMountedFalseResponse(t *testing.T) {
|
|
| 184 |
+ d := newDriver(t) |
|
| 185 |
+ defer os.RemoveAll(tmp) |
|
| 186 |
+ |
|
| 187 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 188 |
+ t.Fatal(err) |
|
| 189 |
+ } |
|
| 190 |
+ |
|
| 191 |
+ response, err := d.mounted("1")
|
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ t.Fatal(err) |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ if response != false {
|
|
| 197 |
+ t.Fatalf("Response if dir id 1 is mounted should be false")
|
|
| 198 |
+ } |
|
| 199 |
+} |
|
| 200 |
+ |
|
| 201 |
+func TestMountedTrueReponse(t *testing.T) {
|
|
| 202 |
+ d := newDriver(t) |
|
| 203 |
+ defer os.RemoveAll(tmp) |
|
| 204 |
+ defer d.Cleanup() |
|
| 205 |
+ |
|
| 206 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 207 |
+ t.Fatal(err) |
|
| 208 |
+ } |
|
| 209 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 210 |
+ t.Fatal(err) |
|
| 211 |
+ } |
|
| 212 |
+ |
|
| 213 |
+ _, err := d.Get("2")
|
|
| 214 |
+ if err != nil {
|
|
| 215 |
+ t.Fatal(err) |
|
| 216 |
+ } |
|
| 217 |
+ |
|
| 218 |
+ response, err := d.mounted("2")
|
|
| 219 |
+ if err != nil {
|
|
| 220 |
+ t.Fatal(err) |
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ if response != true {
|
|
| 224 |
+ t.Fatalf("Response if dir id 2 is mounted should be true")
|
|
| 225 |
+ } |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+func TestMountWithParent(t *testing.T) {
|
|
| 229 |
+ d := newDriver(t) |
|
| 230 |
+ defer os.RemoveAll(tmp) |
|
| 231 |
+ |
|
| 232 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 233 |
+ t.Fatal(err) |
|
| 234 |
+ } |
|
| 235 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 236 |
+ t.Fatal(err) |
|
| 237 |
+ } |
|
| 238 |
+ |
|
| 239 |
+ defer func() {
|
|
| 240 |
+ if err := d.Cleanup(); err != nil {
|
|
| 241 |
+ t.Fatal(err) |
|
| 242 |
+ } |
|
| 243 |
+ }() |
|
| 244 |
+ |
|
| 245 |
+ mntPath, err := d.Get("2")
|
|
| 246 |
+ if err != nil {
|
|
| 247 |
+ t.Fatal(err) |
|
| 248 |
+ } |
|
| 249 |
+ if mntPath == "" {
|
|
| 250 |
+ t.Fatal("mntPath should not be empty string")
|
|
| 251 |
+ } |
|
| 252 |
+ |
|
| 253 |
+ expected := path.Join(tmp, "mnt", "2") |
|
| 254 |
+ if mntPath != expected {
|
|
| 255 |
+ t.Fatalf("Expected %s got %s", expected, mntPath)
|
|
| 256 |
+ } |
|
| 257 |
+} |
|
| 258 |
+ |
|
| 259 |
+func TestRemoveMountedDir(t *testing.T) {
|
|
| 260 |
+ d := newDriver(t) |
|
| 261 |
+ defer os.RemoveAll(tmp) |
|
| 262 |
+ |
|
| 263 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 264 |
+ t.Fatal(err) |
|
| 265 |
+ } |
|
| 266 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 267 |
+ t.Fatal(err) |
|
| 268 |
+ } |
|
| 269 |
+ |
|
| 270 |
+ defer func() {
|
|
| 271 |
+ if err := d.Cleanup(); err != nil {
|
|
| 272 |
+ t.Fatal(err) |
|
| 273 |
+ } |
|
| 274 |
+ }() |
|
| 275 |
+ |
|
| 276 |
+ mntPath, err := d.Get("2")
|
|
| 277 |
+ if err != nil {
|
|
| 278 |
+ t.Fatal(err) |
|
| 279 |
+ } |
|
| 280 |
+ if mntPath == "" {
|
|
| 281 |
+ t.Fatal("mntPath should not be empty string")
|
|
| 282 |
+ } |
|
| 283 |
+ |
|
| 284 |
+ mounted, err := d.mounted("2")
|
|
| 285 |
+ if err != nil {
|
|
| 286 |
+ t.Fatal(err) |
|
| 287 |
+ } |
|
| 288 |
+ |
|
| 289 |
+ if !mounted {
|
|
| 290 |
+ t.Fatalf("Dir id 2 should be mounted")
|
|
| 291 |
+ } |
|
| 292 |
+ |
|
| 293 |
+ if err := d.Remove("2"); err != nil {
|
|
| 294 |
+ t.Fatal(err) |
|
| 295 |
+ } |
|
| 296 |
+} |
|
| 297 |
+ |
|
| 298 |
+func TestCreateWithInvalidParent(t *testing.T) {
|
|
| 299 |
+ d := newDriver(t) |
|
| 300 |
+ defer os.RemoveAll(tmp) |
|
| 301 |
+ |
|
| 302 |
+ if err := d.Create("1", "docker"); err == nil {
|
|
| 303 |
+ t.Fatalf("Error should not be nil with parent does not exist")
|
|
| 304 |
+ } |
|
| 305 |
+} |
|
| 306 |
+ |
|
| 307 |
+func TestGetDiff(t *testing.T) {
|
|
| 308 |
+ d := newDriver(t) |
|
| 309 |
+ defer os.RemoveAll(tmp) |
|
| 310 |
+ |
|
| 311 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 312 |
+ t.Fatal(err) |
|
| 313 |
+ } |
|
| 314 |
+ |
|
| 315 |
+ diffPath, err := d.Get("1")
|
|
| 316 |
+ if err != nil {
|
|
| 317 |
+ t.Fatal(err) |
|
| 318 |
+ } |
|
| 319 |
+ |
|
| 320 |
+ // Add a file to the diff path with a fixed size |
|
| 321 |
+ size := int64(1024) |
|
| 322 |
+ |
|
| 323 |
+ f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 324 |
+ if err != nil {
|
|
| 325 |
+ t.Fatal(err) |
|
| 326 |
+ } |
|
| 327 |
+ if err := f.Truncate(size); err != nil {
|
|
| 328 |
+ t.Fatal(err) |
|
| 329 |
+ } |
|
| 330 |
+ f.Close() |
|
| 331 |
+ |
|
| 332 |
+ a, err := d.Diff("1")
|
|
| 333 |
+ if err != nil {
|
|
| 334 |
+ t.Fatal(err) |
|
| 335 |
+ } |
|
| 336 |
+ if a == nil {
|
|
| 337 |
+ t.Fatalf("Archive should not be nil")
|
|
| 338 |
+ } |
|
| 339 |
+} |
|
| 340 |
+ |
|
| 341 |
+func TestChanges(t *testing.T) {
|
|
| 342 |
+ d := newDriver(t) |
|
| 343 |
+ defer os.RemoveAll(tmp) |
|
| 344 |
+ |
|
| 345 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 346 |
+ t.Fatal(err) |
|
| 347 |
+ } |
|
| 348 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 349 |
+ t.Fatal(err) |
|
| 350 |
+ } |
|
| 351 |
+ |
|
| 352 |
+ defer func() {
|
|
| 353 |
+ if err := d.Cleanup(); err != nil {
|
|
| 354 |
+ t.Fatal(err) |
|
| 355 |
+ } |
|
| 356 |
+ }() |
|
| 357 |
+ |
|
| 358 |
+ mntPoint, err := d.Get("2")
|
|
| 359 |
+ if err != nil {
|
|
| 360 |
+ t.Fatal(err) |
|
| 361 |
+ } |
|
| 362 |
+ |
|
| 363 |
+ // Create a file to save in the mountpoint |
|
| 364 |
+ f, err := os.Create(path.Join(mntPoint, "test.txt")) |
|
| 365 |
+ if err != nil {
|
|
| 366 |
+ t.Fatal(err) |
|
| 367 |
+ } |
|
| 368 |
+ |
|
| 369 |
+ if _, err := f.WriteString("testline"); err != nil {
|
|
| 370 |
+ t.Fatal(err) |
|
| 371 |
+ } |
|
| 372 |
+ if err := f.Close(); err != nil {
|
|
| 373 |
+ t.Fatal(err) |
|
| 374 |
+ } |
|
| 375 |
+ |
|
| 376 |
+ changes, err := d.Changes("2")
|
|
| 377 |
+ if err != nil {
|
|
| 378 |
+ t.Fatal(err) |
|
| 379 |
+ } |
|
| 380 |
+ if len(changes) != 1 {
|
|
| 381 |
+ t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 382 |
+ } |
|
| 383 |
+ change := changes[0] |
|
| 384 |
+ |
|
| 385 |
+ expectedPath := "/test.txt" |
|
| 386 |
+ if change.Path != expectedPath {
|
|
| 387 |
+ t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 388 |
+ } |
|
| 389 |
+ |
|
| 390 |
+ if change.Kind != archive.ChangeAdd {
|
|
| 391 |
+ t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 392 |
+ } |
|
| 393 |
+ |
|
| 394 |
+ if err := d.Create("3", "2"); err != nil {
|
|
| 395 |
+ t.Fatal(err) |
|
| 396 |
+ } |
|
| 397 |
+ mntPoint, err = d.Get("3")
|
|
| 398 |
+ if err != nil {
|
|
| 399 |
+ t.Fatal(err) |
|
| 400 |
+ } |
|
| 401 |
+ |
|
| 402 |
+ // Create a file to save in the mountpoint |
|
| 403 |
+ f, err = os.Create(path.Join(mntPoint, "test2.txt")) |
|
| 404 |
+ if err != nil {
|
|
| 405 |
+ t.Fatal(err) |
|
| 406 |
+ } |
|
| 407 |
+ |
|
| 408 |
+ if _, err := f.WriteString("testline"); err != nil {
|
|
| 409 |
+ t.Fatal(err) |
|
| 410 |
+ } |
|
| 411 |
+ if err := f.Close(); err != nil {
|
|
| 412 |
+ t.Fatal(err) |
|
| 413 |
+ } |
|
| 414 |
+ |
|
| 415 |
+ changes, err = d.Changes("3")
|
|
| 416 |
+ if err != nil {
|
|
| 417 |
+ t.Fatal(err) |
|
| 418 |
+ } |
|
| 419 |
+ |
|
| 420 |
+ if len(changes) != 1 {
|
|
| 421 |
+ t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
|
|
| 422 |
+ } |
|
| 423 |
+ change = changes[0] |
|
| 424 |
+ |
|
| 425 |
+ expectedPath = "/test2.txt" |
|
| 426 |
+ if change.Path != expectedPath {
|
|
| 427 |
+ t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
|
|
| 428 |
+ } |
|
| 429 |
+ |
|
| 430 |
+ if change.Kind != archive.ChangeAdd {
|
|
| 431 |
+ t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
|
|
| 432 |
+ } |
|
| 433 |
+} |
|
| 434 |
+ |
|
| 435 |
+func TestDiffSize(t *testing.T) {
|
|
| 436 |
+ d := newDriver(t) |
|
| 437 |
+ defer os.RemoveAll(tmp) |
|
| 438 |
+ |
|
| 439 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 440 |
+ t.Fatal(err) |
|
| 441 |
+ } |
|
| 442 |
+ |
|
| 443 |
+ diffPath, err := d.Get("1")
|
|
| 444 |
+ if err != nil {
|
|
| 445 |
+ t.Fatal(err) |
|
| 446 |
+ } |
|
| 447 |
+ |
|
| 448 |
+ // Add a file to the diff path with a fixed size |
|
| 449 |
+ size := int64(1024) |
|
| 450 |
+ |
|
| 451 |
+ f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 452 |
+ if err != nil {
|
|
| 453 |
+ t.Fatal(err) |
|
| 454 |
+ } |
|
| 455 |
+ if err := f.Truncate(size); err != nil {
|
|
| 456 |
+ t.Fatal(err) |
|
| 457 |
+ } |
|
| 458 |
+ s, err := f.Stat() |
|
| 459 |
+ if err != nil {
|
|
| 460 |
+ t.Fatal(err) |
|
| 461 |
+ } |
|
| 462 |
+ size = s.Size() |
|
| 463 |
+ if err := f.Close(); err != nil {
|
|
| 464 |
+ t.Fatal(err) |
|
| 465 |
+ } |
|
| 466 |
+ |
|
| 467 |
+ diffSize, err := d.DiffSize("1")
|
|
| 468 |
+ if err != nil {
|
|
| 469 |
+ t.Fatal(err) |
|
| 470 |
+ } |
|
| 471 |
+ if diffSize != size {
|
|
| 472 |
+ t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
| 473 |
+ } |
|
| 474 |
+} |
|
| 475 |
+ |
|
| 476 |
+func TestChildDiffSize(t *testing.T) {
|
|
| 477 |
+ d := newDriver(t) |
|
| 478 |
+ defer os.RemoveAll(tmp) |
|
| 479 |
+ defer d.Cleanup() |
|
| 480 |
+ |
|
| 481 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 482 |
+ t.Fatal(err) |
|
| 483 |
+ } |
|
| 484 |
+ |
|
| 485 |
+ diffPath, err := d.Get("1")
|
|
| 486 |
+ if err != nil {
|
|
| 487 |
+ t.Fatal(err) |
|
| 488 |
+ } |
|
| 489 |
+ |
|
| 490 |
+ // Add a file to the diff path with a fixed size |
|
| 491 |
+ size := int64(1024) |
|
| 492 |
+ |
|
| 493 |
+ f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 494 |
+ if err != nil {
|
|
| 495 |
+ t.Fatal(err) |
|
| 496 |
+ } |
|
| 497 |
+ if err := f.Truncate(size); err != nil {
|
|
| 498 |
+ t.Fatal(err) |
|
| 499 |
+ } |
|
| 500 |
+ s, err := f.Stat() |
|
| 501 |
+ if err != nil {
|
|
| 502 |
+ t.Fatal(err) |
|
| 503 |
+ } |
|
| 504 |
+ size = s.Size() |
|
| 505 |
+ if err := f.Close(); err != nil {
|
|
| 506 |
+ t.Fatal(err) |
|
| 507 |
+ } |
|
| 508 |
+ |
|
| 509 |
+ diffSize, err := d.DiffSize("1")
|
|
| 510 |
+ if err != nil {
|
|
| 511 |
+ t.Fatal(err) |
|
| 512 |
+ } |
|
| 513 |
+ if diffSize != size {
|
|
| 514 |
+ t.Fatalf("Expected size to be %d got %d", size, diffSize)
|
|
| 515 |
+ } |
|
| 516 |
+ |
|
| 517 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 518 |
+ t.Fatal(err) |
|
| 519 |
+ } |
|
| 520 |
+ |
|
| 521 |
+ diffSize, err = d.DiffSize("2")
|
|
| 522 |
+ if err != nil {
|
|
| 523 |
+ t.Fatal(err) |
|
| 524 |
+ } |
|
| 525 |
+ // The diff size for the child should be zero |
|
| 526 |
+ if diffSize != 0 {
|
|
| 527 |
+ t.Fatalf("Expected size to be %d got %d", 0, diffSize)
|
|
| 528 |
+ } |
|
| 529 |
+} |
|
| 530 |
+ |
|
| 531 |
+func TestExists(t *testing.T) {
|
|
| 532 |
+ d := newDriver(t) |
|
| 533 |
+ defer os.RemoveAll(tmp) |
|
| 534 |
+ defer d.Cleanup() |
|
| 535 |
+ |
|
| 536 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 537 |
+ t.Fatal(err) |
|
| 538 |
+ } |
|
| 539 |
+ |
|
| 540 |
+ if d.Exists("none") {
|
|
| 541 |
+ t.Fatal("id name should not exist in the driver")
|
|
| 542 |
+ } |
|
| 543 |
+ |
|
| 544 |
+ if !d.Exists("1") {
|
|
| 545 |
+ t.Fatal("id 1 should exist in the driver")
|
|
| 546 |
+ } |
|
| 547 |
+} |
|
| 548 |
+ |
|
| 549 |
+func TestStatus(t *testing.T) {
|
|
| 550 |
+ d := newDriver(t) |
|
| 551 |
+ defer os.RemoveAll(tmp) |
|
| 552 |
+ defer d.Cleanup() |
|
| 553 |
+ |
|
| 554 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 555 |
+ t.Fatal(err) |
|
| 556 |
+ } |
|
| 557 |
+ |
|
| 558 |
+ status := d.Status() |
|
| 559 |
+ if status == nil || len(status) == 0 {
|
|
| 560 |
+ t.Fatal("Status should not be nil or empty")
|
|
| 561 |
+ } |
|
| 562 |
+ rootDir := status[0] |
|
| 563 |
+ dirs := status[1] |
|
| 564 |
+ if rootDir[0] != "Root Dir" {
|
|
| 565 |
+ t.Fatalf("Expected Root Dir got %s", rootDir[0])
|
|
| 566 |
+ } |
|
| 567 |
+ if rootDir[1] != d.rootPath() {
|
|
| 568 |
+ t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
|
|
| 569 |
+ } |
|
| 570 |
+ if dirs[0] != "Dirs" {
|
|
| 571 |
+ t.Fatalf("Expected Dirs got %s", dirs[0])
|
|
| 572 |
+ } |
|
| 573 |
+ if dirs[1] != "1" {
|
|
| 574 |
+ t.Fatalf("Expected 1 got %s", dirs[1])
|
|
| 575 |
+ } |
|
| 576 |
+} |
|
| 577 |
+ |
|
| 578 |
+func TestApplyDiff(t *testing.T) {
|
|
| 579 |
+ d := newDriver(t) |
|
| 580 |
+ defer os.RemoveAll(tmp) |
|
| 581 |
+ defer d.Cleanup() |
|
| 582 |
+ |
|
| 583 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 584 |
+ t.Fatal(err) |
|
| 585 |
+ } |
|
| 586 |
+ |
|
| 587 |
+ diffPath, err := d.Get("1")
|
|
| 588 |
+ if err != nil {
|
|
| 589 |
+ t.Fatal(err) |
|
| 590 |
+ } |
|
| 591 |
+ |
|
| 592 |
+ // Add a file to the diff path with a fixed size |
|
| 593 |
+ size := int64(1024) |
|
| 594 |
+ |
|
| 595 |
+ f, err := os.Create(path.Join(diffPath, "test_file")) |
|
| 596 |
+ if err != nil {
|
|
| 597 |
+ t.Fatal(err) |
|
| 598 |
+ } |
|
| 599 |
+ if err := f.Truncate(size); err != nil {
|
|
| 600 |
+ t.Fatal(err) |
|
| 601 |
+ } |
|
| 602 |
+ f.Close() |
|
| 603 |
+ |
|
| 604 |
+ diff, err := d.Diff("1")
|
|
| 605 |
+ if err != nil {
|
|
| 606 |
+ t.Fatal(err) |
|
| 607 |
+ } |
|
| 608 |
+ |
|
| 609 |
+ if err := d.Create("2", ""); err != nil {
|
|
| 610 |
+ t.Fatal(err) |
|
| 611 |
+ } |
|
| 612 |
+ if err := d.Create("3", "2"); err != nil {
|
|
| 613 |
+ t.Fatal(err) |
|
| 614 |
+ } |
|
| 615 |
+ |
|
| 616 |
+ if err := d.ApplyDiff("3", diff); err != nil {
|
|
| 617 |
+ t.Fatal(err) |
|
| 618 |
+ } |
|
| 619 |
+ |
|
| 620 |
+ // Ensure that the file is in the mount point for id 3 |
|
| 621 |
+ |
|
| 622 |
+ mountPoint, err := d.Get("3")
|
|
| 623 |
+ if err != nil {
|
|
| 624 |
+ t.Fatal(err) |
|
| 625 |
+ } |
|
| 626 |
+ if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
|
|
| 627 |
+ t.Fatal(err) |
|
| 628 |
+ } |
|
| 629 |
+} |
|
| 630 |
+ |
|
| 631 |
+func hash(c string) string {
|
|
| 632 |
+ h := sha256.New() |
|
| 633 |
+ fmt.Fprint(h, c) |
|
| 634 |
+ return hex.EncodeToString(h.Sum(nil)) |
|
| 635 |
+} |
|
| 636 |
+ |
|
| 637 |
+func TestMountMoreThan42Layers(t *testing.T) {
|
|
| 638 |
+ d := newDriver(t) |
|
| 639 |
+ defer os.RemoveAll(tmp) |
|
| 640 |
+ defer d.Cleanup() |
|
| 641 |
+ var last string |
|
| 642 |
+ var expected int |
|
| 643 |
+ |
|
| 644 |
+ for i := 1; i < 127; i++ {
|
|
| 645 |
+ expected++ |
|
| 646 |
+ var ( |
|
| 647 |
+ parent = fmt.Sprintf("%d", i-1)
|
|
| 648 |
+ current = fmt.Sprintf("%d", i)
|
|
| 649 |
+ ) |
|
| 650 |
+ |
|
| 651 |
+ if parent == "0" {
|
|
| 652 |
+ parent = "" |
|
| 653 |
+ } else {
|
|
| 654 |
+ parent = hash(parent) |
|
| 655 |
+ } |
|
| 656 |
+ current = hash(current) |
|
| 657 |
+ |
|
| 658 |
+ if err := d.Create(current, parent); err != nil {
|
|
| 659 |
+ t.Logf("Current layer %d", i)
|
|
| 660 |
+ t.Fatal(err) |
|
| 661 |
+ } |
|
| 662 |
+ point, err := d.Get(current) |
|
| 663 |
+ if err != nil {
|
|
| 664 |
+ t.Logf("Current layer %d", i)
|
|
| 665 |
+ t.Fatal(err) |
|
| 666 |
+ } |
|
| 667 |
+ f, err := os.Create(path.Join(point, current)) |
|
| 668 |
+ if err != nil {
|
|
| 669 |
+ t.Logf("Current layer %d", i)
|
|
| 670 |
+ t.Fatal(err) |
|
| 671 |
+ } |
|
| 672 |
+ f.Close() |
|
| 673 |
+ |
|
| 674 |
+ if i%10 == 0 {
|
|
| 675 |
+ if err := os.Remove(path.Join(point, parent)); err != nil {
|
|
| 676 |
+ t.Logf("Current layer %d", i)
|
|
| 677 |
+ t.Fatal(err) |
|
| 678 |
+ } |
|
| 679 |
+ expected-- |
|
| 680 |
+ } |
|
| 681 |
+ last = current |
|
| 682 |
+ } |
|
| 683 |
+ |
|
| 684 |
+ // Perform the actual mount for the top most image |
|
| 685 |
+ point, err := d.Get(last) |
|
| 686 |
+ if err != nil {
|
|
| 687 |
+ t.Fatal(err) |
|
| 688 |
+ } |
|
| 689 |
+ files, err := ioutil.ReadDir(point) |
|
| 690 |
+ if err != nil {
|
|
| 691 |
+ t.Fatal(err) |
|
| 692 |
+ } |
|
| 693 |
+ if len(files) != expected {
|
|
| 694 |
+ t.Fatalf("Expected %d got %d", expected, len(files))
|
|
| 695 |
+ } |
|
| 696 |
+} |
| 0 | 697 |
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,194 @@ |
| 0 |
+package aufs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "io/ioutil" |
|
| 6 |
+ "os" |
|
| 7 |
+ "path" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type metadata struct {
|
|
| 11 |
+ ID string `json:"id"` |
|
| 12 |
+ ParentID string `json:"parent,omitempty"` |
|
| 13 |
+ Image string `json:"Image,omitempty"` |
|
| 14 |
+ |
|
| 15 |
+ parent *metadata |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func pathExists(pth string) bool {
|
|
| 19 |
+ if _, err := os.Stat(pth); err != nil {
|
|
| 20 |
+ return false |
|
| 21 |
+ } |
|
| 22 |
+ return true |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// Migrate existing images and containers from docker < 0.7.x |
|
| 26 |
+// |
|
| 27 |
+// The format pre 0.7 is for docker to store the metadata and filesystem |
|
| 28 |
+// content in the same directory. For the migration to work we need to move Image layer |
|
| 29 |
+// data from /var/lib/docker/graph/<id>/layers to the diff of the registered id. |
|
| 30 |
+// |
|
| 31 |
+// Next we need to migrate the container's rw layer to diff of the driver. After the |
|
| 32 |
+// contents are migrated we need to register the image and container ids with the |
|
| 33 |
+// driver. |
|
| 34 |
+// |
|
| 35 |
+// For the migration we try to move the folder containing the layer files, if that |
|
| 36 |
+// fails because the data is currently mounted we will fallback to creating a |
|
| 37 |
+// symlink. |
|
| 38 |
+func (a *Driver) Migrate(pth string, setupInit func(p string) error) error {
|
|
| 39 |
+ if pathExists(path.Join(pth, "graph")) {
|
|
| 40 |
+ if err := a.migrateRepositories(pth); err != nil {
|
|
| 41 |
+ return err |
|
| 42 |
+ } |
|
| 43 |
+ if err := a.migrateImages(path.Join(pth, "graph")); err != nil {
|
|
| 44 |
+ return err |
|
| 45 |
+ } |
|
| 46 |
+ return a.migrateContainers(path.Join(pth, "containers"), setupInit) |
|
| 47 |
+ } |
|
| 48 |
+ return nil |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func (a *Driver) migrateRepositories(pth string) error {
|
|
| 52 |
+ name := path.Join(pth, "repositories") |
|
| 53 |
+ if err := os.Rename(name, name+"-aufs"); err != nil && !os.IsNotExist(err) {
|
|
| 54 |
+ return err |
|
| 55 |
+ } |
|
| 56 |
+ return nil |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func (a *Driver) migrateContainers(pth string, setupInit func(p string) error) error {
|
|
| 60 |
+ fis, err := ioutil.ReadDir(pth) |
|
| 61 |
+ if err != nil {
|
|
| 62 |
+ return err |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ for _, fi := range fis {
|
|
| 66 |
+ if id := fi.Name(); fi.IsDir() && pathExists(path.Join(pth, id, "rw")) {
|
|
| 67 |
+ if err := tryRelocate(path.Join(pth, id, "rw"), path.Join(a.rootPath(), "diff", id)); err != nil {
|
|
| 68 |
+ return err |
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ if !a.Exists(id) {
|
|
| 72 |
+ |
|
| 73 |
+ metadata, err := loadMetadata(path.Join(pth, id, "config.json")) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ return err |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ initID := fmt.Sprintf("%s-init", id)
|
|
| 79 |
+ if err := a.Create(initID, metadata.Image); err != nil {
|
|
| 80 |
+ return err |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ initPath, err := a.Get(initID) |
|
| 84 |
+ if err != nil {
|
|
| 85 |
+ return err |
|
| 86 |
+ } |
|
| 87 |
+ // setup init layer |
|
| 88 |
+ if err := setupInit(initPath); err != nil {
|
|
| 89 |
+ return err |
|
| 90 |
+ } |
|
| 91 |
+ |
|
| 92 |
+ if err := a.Create(id, initID); err != nil {
|
|
| 93 |
+ return err |
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+ } |
|
| 97 |
+ } |
|
| 98 |
+ return nil |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+func (a *Driver) migrateImages(pth string) error {
|
|
| 102 |
+ fis, err := ioutil.ReadDir(pth) |
|
| 103 |
+ if err != nil {
|
|
| 104 |
+ return err |
|
| 105 |
+ } |
|
| 106 |
+ var ( |
|
| 107 |
+ m = make(map[string]*metadata) |
|
| 108 |
+ current *metadata |
|
| 109 |
+ exists bool |
|
| 110 |
+ ) |
|
| 111 |
+ |
|
| 112 |
+ for _, fi := range fis {
|
|
| 113 |
+ if id := fi.Name(); fi.IsDir() && pathExists(path.Join(pth, id, "layer")) {
|
|
| 114 |
+ if current, exists = m[id]; !exists {
|
|
| 115 |
+ current, err = loadMetadata(path.Join(pth, id, "json")) |
|
| 116 |
+ if err != nil {
|
|
| 117 |
+ return err |
|
| 118 |
+ } |
|
| 119 |
+ m[id] = current |
|
| 120 |
+ } |
|
| 121 |
+ } |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ for _, v := range m {
|
|
| 125 |
+ v.parent = m[v.ParentID] |
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ migrated := make(map[string]bool) |
|
| 129 |
+ for _, v := range m {
|
|
| 130 |
+ if err := a.migrateImage(v, pth, migrated); err != nil {
|
|
| 131 |
+ return err |
|
| 132 |
+ } |
|
| 133 |
+ } |
|
| 134 |
+ return nil |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+func (a *Driver) migrateImage(m *metadata, pth string, migrated map[string]bool) error {
|
|
| 138 |
+ if !migrated[m.ID] {
|
|
| 139 |
+ if m.parent != nil {
|
|
| 140 |
+ a.migrateImage(m.parent, pth, migrated) |
|
| 141 |
+ } |
|
| 142 |
+ if err := tryRelocate(path.Join(pth, m.ID, "layer"), path.Join(a.rootPath(), "diff", m.ID)); err != nil {
|
|
| 143 |
+ return err |
|
| 144 |
+ } |
|
| 145 |
+ if !a.Exists(m.ID) {
|
|
| 146 |
+ if err := a.Create(m.ID, m.ParentID); err != nil {
|
|
| 147 |
+ return err |
|
| 148 |
+ } |
|
| 149 |
+ } |
|
| 150 |
+ migrated[m.ID] = true |
|
| 151 |
+ } |
|
| 152 |
+ return nil |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+// tryRelocate will try to rename the old path to the new pack and if |
|
| 156 |
+// the operation fails, it will fallback to a symlink |
|
| 157 |
+func tryRelocate(oldPath, newPath string) error {
|
|
| 158 |
+ s, err := os.Lstat(newPath) |
|
| 159 |
+ if err != nil && !os.IsNotExist(err) {
|
|
| 160 |
+ return err |
|
| 161 |
+ } |
|
| 162 |
+ // If the destination is a symlink then we already tried to relocate once before |
|
| 163 |
+ // and it failed so we delete it and try to remove |
|
| 164 |
+ if s != nil && s.Mode()&os.ModeSymlink == os.ModeSymlink {
|
|
| 165 |
+ if err := os.RemoveAll(newPath); err != nil {
|
|
| 166 |
+ return err |
|
| 167 |
+ } |
|
| 168 |
+ } |
|
| 169 |
+ if err := os.Rename(oldPath, newPath); err != nil {
|
|
| 170 |
+ if sErr := os.Symlink(oldPath, newPath); sErr != nil {
|
|
| 171 |
+ return fmt.Errorf("Unable to relocate %s to %s: Rename err %s Symlink err %s", oldPath, newPath, err, sErr)
|
|
| 172 |
+ } |
|
| 173 |
+ } |
|
| 174 |
+ return nil |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func loadMetadata(pth string) (*metadata, error) {
|
|
| 178 |
+ f, err := os.Open(pth) |
|
| 179 |
+ if err != nil {
|
|
| 180 |
+ return nil, err |
|
| 181 |
+ } |
|
| 182 |
+ defer f.Close() |
|
| 183 |
+ |
|
| 184 |
+ var ( |
|
| 185 |
+ out = &metadata{}
|
|
| 186 |
+ dec = json.NewDecoder(f) |
|
| 187 |
+ ) |
|
| 188 |
+ |
|
| 189 |
+ if err := dec.Decode(out); err != nil {
|
|
| 190 |
+ return nil, err |
|
| 191 |
+ } |
|
| 192 |
+ return out, nil |
|
| 193 |
+} |
| 0 | 194 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,17 @@ |
| 0 |
+package aufs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/utils" |
|
| 4 |
+ "os/exec" |
|
| 5 |
+ "syscall" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func Unmount(target string) error {
|
|
| 9 |
+ if err := exec.Command("auplink", target, "flush").Run(); err != nil {
|
|
| 10 |
+ utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
|
|
| 11 |
+ } |
|
| 12 |
+ if err := syscall.Unmount(target, 0); err != nil {
|
|
| 13 |
+ return err |
|
| 14 |
+ } |
|
| 15 |
+ return nil |
|
| 16 |
+} |
| 0 | 17 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+// +build amd64 |
|
| 1 |
+ |
|
| 2 |
+package aufs |
|
| 3 |
+ |
|
| 4 |
+import "syscall" |
|
| 5 |
+ |
|
| 6 |
+const MsRemount = syscall.MS_REMOUNT |
|
| 7 |
+ |
|
| 8 |
+func mount(source string, target string, fstype string, flags uintptr, data string) error {
|
|
| 9 |
+ return syscall.Mount(source, target, fstype, flags, data) |
|
| 10 |
+} |
| 0 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+// +build !linux !amd64 |
|
| 1 |
+ |
|
| 2 |
+package aufs |
|
| 3 |
+ |
|
| 4 |
+import "errors" |
|
| 5 |
+ |
|
| 6 |
+const MsRemount = 0 |
|
| 7 |
+ |
|
| 8 |
+func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
|
|
| 9 |
+ return errors.New("mount is not implemented on darwin")
|
|
| 10 |
+} |
| 0 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,213 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package btrfs |
|
| 3 |
+ |
|
| 4 |
+/* |
|
| 5 |
+#include <stdlib.h> |
|
| 6 |
+#include <dirent.h> |
|
| 7 |
+#include <btrfs/ioctl.h> |
|
| 8 |
+*/ |
|
| 9 |
+import "C" |
|
| 10 |
+ |
|
| 11 |
+import ( |
|
| 12 |
+ "fmt" |
|
| 13 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 14 |
+ "os" |
|
| 15 |
+ "path" |
|
| 16 |
+ "syscall" |
|
| 17 |
+ "unsafe" |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+func init() {
|
|
| 21 |
+ graphdriver.Register("btrfs", Init)
|
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func Init(home string) (graphdriver.Driver, error) {
|
|
| 25 |
+ rootdir := path.Dir(home) |
|
| 26 |
+ |
|
| 27 |
+ var buf syscall.Statfs_t |
|
| 28 |
+ if err := syscall.Statfs(rootdir, &buf); err != nil {
|
|
| 29 |
+ return nil, err |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ if buf.Type != 0x9123683E {
|
|
| 33 |
+ return nil, fmt.Errorf("%s is not a btrfs filesystem", rootdir)
|
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ return &Driver{
|
|
| 37 |
+ home: home, |
|
| 38 |
+ }, nil |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+type Driver struct {
|
|
| 42 |
+ home string |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+func (d *Driver) String() string {
|
|
| 46 |
+ return "btrfs" |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func (d *Driver) Status() [][2]string {
|
|
| 50 |
+ return nil |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func (d *Driver) Cleanup() error {
|
|
| 54 |
+ return nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func free(p *C.char) {
|
|
| 58 |
+ C.free(unsafe.Pointer(p)) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func openDir(path string) (*C.DIR, error) {
|
|
| 62 |
+ Cpath := C.CString(path) |
|
| 63 |
+ defer free(Cpath) |
|
| 64 |
+ |
|
| 65 |
+ dir := C.opendir(Cpath) |
|
| 66 |
+ if dir == nil {
|
|
| 67 |
+ return nil, fmt.Errorf("Can't open dir")
|
|
| 68 |
+ } |
|
| 69 |
+ return dir, nil |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func closeDir(dir *C.DIR) {
|
|
| 73 |
+ if dir != nil {
|
|
| 74 |
+ C.closedir(dir) |
|
| 75 |
+ } |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+func getDirFd(dir *C.DIR) uintptr {
|
|
| 79 |
+ return uintptr(C.dirfd(dir)) |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func subvolCreate(path, name string) error {
|
|
| 83 |
+ dir, err := openDir(path) |
|
| 84 |
+ if err != nil {
|
|
| 85 |
+ return err |
|
| 86 |
+ } |
|
| 87 |
+ defer closeDir(dir) |
|
| 88 |
+ |
|
| 89 |
+ var args C.struct_btrfs_ioctl_vol_args |
|
| 90 |
+ for i, c := range []byte(name) {
|
|
| 91 |
+ args.name[i] = C.char(c) |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE, |
|
| 95 |
+ uintptr(unsafe.Pointer(&args))) |
|
| 96 |
+ if errno != 0 {
|
|
| 97 |
+ return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
|
|
| 98 |
+ } |
|
| 99 |
+ return nil |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+func subvolSnapshot(src, dest, name string) error {
|
|
| 103 |
+ srcDir, err := openDir(src) |
|
| 104 |
+ if err != nil {
|
|
| 105 |
+ return err |
|
| 106 |
+ } |
|
| 107 |
+ defer closeDir(srcDir) |
|
| 108 |
+ |
|
| 109 |
+ destDir, err := openDir(dest) |
|
| 110 |
+ if err != nil {
|
|
| 111 |
+ return err |
|
| 112 |
+ } |
|
| 113 |
+ defer closeDir(destDir) |
|
| 114 |
+ |
|
| 115 |
+ var args C.struct_btrfs_ioctl_vol_args_v2 |
|
| 116 |
+ args.fd = C.__s64(getDirFd(srcDir)) |
|
| 117 |
+ for i, c := range []byte(name) {
|
|
| 118 |
+ args.name[i] = C.char(c) |
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2, |
|
| 122 |
+ uintptr(unsafe.Pointer(&args))) |
|
| 123 |
+ if errno != 0 {
|
|
| 124 |
+ return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
|
|
| 125 |
+ } |
|
| 126 |
+ return nil |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+func subvolDelete(path, name string) error {
|
|
| 130 |
+ dir, err := openDir(path) |
|
| 131 |
+ if err != nil {
|
|
| 132 |
+ return err |
|
| 133 |
+ } |
|
| 134 |
+ defer closeDir(dir) |
|
| 135 |
+ |
|
| 136 |
+ var args C.struct_btrfs_ioctl_vol_args |
|
| 137 |
+ for i, c := range []byte(name) {
|
|
| 138 |
+ args.name[i] = C.char(c) |
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY, |
|
| 142 |
+ uintptr(unsafe.Pointer(&args))) |
|
| 143 |
+ if errno != 0 {
|
|
| 144 |
+ return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error())
|
|
| 145 |
+ } |
|
| 146 |
+ return nil |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+func (d *Driver) subvolumesDir() string {
|
|
| 150 |
+ return path.Join(d.home, "subvolumes") |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+func (d *Driver) subvolumesDirId(id string) string {
|
|
| 154 |
+ return path.Join(d.subvolumesDir(), id) |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+func (d *Driver) Create(id string, parent string) error {
|
|
| 158 |
+ subvolumes := path.Join(d.home, "subvolumes") |
|
| 159 |
+ if err := os.MkdirAll(subvolumes, 0700); err != nil {
|
|
| 160 |
+ return err |
|
| 161 |
+ } |
|
| 162 |
+ if parent == "" {
|
|
| 163 |
+ if err := subvolCreate(subvolumes, id); err != nil {
|
|
| 164 |
+ return err |
|
| 165 |
+ } |
|
| 166 |
+ } else {
|
|
| 167 |
+ parentDir, err := d.Get(parent) |
|
| 168 |
+ if err != nil {
|
|
| 169 |
+ return err |
|
| 170 |
+ } |
|
| 171 |
+ if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
|
|
| 172 |
+ return err |
|
| 173 |
+ } |
|
| 174 |
+ } |
|
| 175 |
+ return nil |
|
| 176 |
+} |
|
| 177 |
+ |
|
| 178 |
+func (d *Driver) Remove(id string) error {
|
|
| 179 |
+ dir := d.subvolumesDirId(id) |
|
| 180 |
+ if _, err := os.Stat(dir); err != nil {
|
|
| 181 |
+ return err |
|
| 182 |
+ } |
|
| 183 |
+ if err := subvolDelete(d.subvolumesDir(), id); err != nil {
|
|
| 184 |
+ return err |
|
| 185 |
+ } |
|
| 186 |
+ return os.RemoveAll(dir) |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+func (d *Driver) Get(id string) (string, error) {
|
|
| 190 |
+ dir := d.subvolumesDirId(id) |
|
| 191 |
+ st, err := os.Stat(dir) |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ return "", err |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ if !st.IsDir() {
|
|
| 197 |
+ return "", fmt.Errorf("%s: not a directory", dir)
|
|
| 198 |
+ } |
|
| 199 |
+ |
|
| 200 |
+ return dir, nil |
|
| 201 |
+} |
|
| 202 |
+ |
|
| 203 |
+func (d *Driver) Put(id string) {
|
|
| 204 |
+ // Get() creates no runtime resources (like e.g. mounts) |
|
| 205 |
+ // so this doesn't need to do anything. |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+func (d *Driver) Exists(id string) bool {
|
|
| 209 |
+ dir := d.subvolumesDirId(id) |
|
| 210 |
+ _, err := os.Stat(dir) |
|
| 211 |
+ return err == nil |
|
| 212 |
+} |
| 0 | 3 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,126 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/utils" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func stringToLoopName(src string) [LoNameSize]uint8 {
|
|
| 10 |
+ var dst [LoNameSize]uint8 |
|
| 11 |
+ copy(dst[:], src[:]) |
|
| 12 |
+ return dst |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+func getNextFreeLoopbackIndex() (int, error) {
|
|
| 16 |
+ f, err := osOpenFile("/dev/loop-control", osORdOnly, 0644)
|
|
| 17 |
+ if err != nil {
|
|
| 18 |
+ return 0, err |
|
| 19 |
+ } |
|
| 20 |
+ defer f.Close() |
|
| 21 |
+ |
|
| 22 |
+ index, err := ioctlLoopCtlGetFree(f.Fd()) |
|
| 23 |
+ if index < 0 {
|
|
| 24 |
+ index = 0 |
|
| 25 |
+ } |
|
| 26 |
+ return index, err |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+func openNextAvailableLoopback(index int, sparseFile *osFile) (loopFile *osFile, err error) {
|
|
| 30 |
+ // Start looking for a free /dev/loop |
|
| 31 |
+ for {
|
|
| 32 |
+ target := fmt.Sprintf("/dev/loop%d", index)
|
|
| 33 |
+ index++ |
|
| 34 |
+ |
|
| 35 |
+ fi, err := osStat(target) |
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ if osIsNotExist(err) {
|
|
| 38 |
+ utils.Errorf("There are no more loopback device available.")
|
|
| 39 |
+ } |
|
| 40 |
+ return nil, ErrAttachLoopbackDevice |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ if fi.Mode()&osModeDevice != osModeDevice {
|
|
| 44 |
+ utils.Errorf("Loopback device %s is not a block device.", target)
|
|
| 45 |
+ continue |
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ // OpenFile adds O_CLOEXEC |
|
| 49 |
+ loopFile, err = osOpenFile(target, osORdWr, 0644) |
|
| 50 |
+ if err != nil {
|
|
| 51 |
+ utils.Errorf("Error openning loopback device: %s", err)
|
|
| 52 |
+ return nil, ErrAttachLoopbackDevice |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ // Try to attach to the loop file |
|
| 56 |
+ if err := ioctlLoopSetFd(loopFile.Fd(), sparseFile.Fd()); err != nil {
|
|
| 57 |
+ loopFile.Close() |
|
| 58 |
+ |
|
| 59 |
+ // If the error is EBUSY, then try the next loopback |
|
| 60 |
+ if err != sysEBusy {
|
|
| 61 |
+ utils.Errorf("Cannot set up loopback device %s: %s", target, err)
|
|
| 62 |
+ return nil, ErrAttachLoopbackDevice |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ // Otherwise, we keep going with the loop |
|
| 66 |
+ continue |
|
| 67 |
+ } |
|
| 68 |
+ // In case of success, we finished. Break the loop. |
|
| 69 |
+ break |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ // This can't happen, but let's be sure |
|
| 73 |
+ if loopFile == nil {
|
|
| 74 |
+ utils.Errorf("Unreachable code reached! Error attaching %s to a loopback device.", sparseFile.Name())
|
|
| 75 |
+ return nil, ErrAttachLoopbackDevice |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ return loopFile, nil |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+// attachLoopDevice attaches the given sparse file to the next |
|
| 82 |
+// available loopback device. It returns an opened *osFile. |
|
| 83 |
+func attachLoopDevice(sparseName string) (loop *osFile, err error) {
|
|
| 84 |
+ |
|
| 85 |
+ // Try to retrieve the next available loopback device via syscall. |
|
| 86 |
+ // If it fails, we discard error and start loopking for a |
|
| 87 |
+ // loopback from index 0. |
|
| 88 |
+ startIndex, err := getNextFreeLoopbackIndex() |
|
| 89 |
+ if err != nil {
|
|
| 90 |
+ utils.Debugf("Error retrieving the next available loopback: %s", err)
|
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ // OpenFile adds O_CLOEXEC |
|
| 94 |
+ sparseFile, err := osOpenFile(sparseName, osORdWr, 0644) |
|
| 95 |
+ if err != nil {
|
|
| 96 |
+ utils.Errorf("Error openning sparse file %s: %s", sparseName, err)
|
|
| 97 |
+ return nil, ErrAttachLoopbackDevice |
|
| 98 |
+ } |
|
| 99 |
+ defer sparseFile.Close() |
|
| 100 |
+ |
|
| 101 |
+ loopFile, err := openNextAvailableLoopback(startIndex, sparseFile) |
|
| 102 |
+ if err != nil {
|
|
| 103 |
+ return nil, err |
|
| 104 |
+ } |
|
| 105 |
+ |
|
| 106 |
+ // Set the status of the loopback device |
|
| 107 |
+ loopInfo := &LoopInfo64{
|
|
| 108 |
+ loFileName: stringToLoopName(loopFile.Name()), |
|
| 109 |
+ loOffset: 0, |
|
| 110 |
+ loFlags: LoFlagsAutoClear, |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil {
|
|
| 114 |
+ utils.Errorf("Cannot set up loopback device info: %s", err)
|
|
| 115 |
+ |
|
| 116 |
+ // If the call failed, then free the loopback device |
|
| 117 |
+ if err := ioctlLoopClrFd(loopFile.Fd()); err != nil {
|
|
| 118 |
+ utils.Errorf("Error while cleaning up the loopback device")
|
|
| 119 |
+ } |
|
| 120 |
+ loopFile.Close() |
|
| 121 |
+ return nil, ErrAttachLoopbackDevice |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ return loopFile, nil |
|
| 125 |
+} |
| 0 | 126 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,1088 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "encoding/json" |
|
| 6 |
+ "errors" |
|
| 7 |
+ "fmt" |
|
| 8 |
+ "github.com/dotcloud/docker/utils" |
|
| 9 |
+ "io" |
|
| 10 |
+ "io/ioutil" |
|
| 11 |
+ "path" |
|
| 12 |
+ "path/filepath" |
|
| 13 |
+ "strconv" |
|
| 14 |
+ "strings" |
|
| 15 |
+ "sync" |
|
| 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 |
+ mountCount int `json:"-"` |
|
| 34 |
+ mountPath string `json:"-"` |
|
| 35 |
+ // A floating mount means one reference is not owned and |
|
| 36 |
+ // will be stolen by the next mount. This allows us to |
|
| 37 |
+ // avoid unmounting directly after creation before the |
|
| 38 |
+ // first get (since we need to mount to set up the device |
|
| 39 |
+ // a bit first). |
|
| 40 |
+ floating bool `json:"-"` |
|
| 41 |
+ |
|
| 42 |
+ // The global DeviceSet lock guarantees that we serialize all |
|
| 43 |
+ // the calls to libdevmapper (which is not threadsafe), but we |
|
| 44 |
+ // sometimes release that lock while sleeping. In that case |
|
| 45 |
+ // this per-device lock is still held, protecting against |
|
| 46 |
+ // other accesses to the device that we're doing the wait on. |
|
| 47 |
+ lock sync.Mutex `json:"-"` |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+type MetaData struct {
|
|
| 51 |
+ Devices map[string]*DevInfo `json:devices` |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+type DeviceSet struct {
|
|
| 55 |
+ MetaData |
|
| 56 |
+ sync.Mutex // Protects Devices map and serializes calls into libdevmapper |
|
| 57 |
+ root string |
|
| 58 |
+ devicePrefix string |
|
| 59 |
+ TransactionId uint64 |
|
| 60 |
+ NewTransactionId uint64 |
|
| 61 |
+ nextFreeDevice int |
|
| 62 |
+ sawBusy bool |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+type DiskUsage struct {
|
|
| 66 |
+ Used uint64 |
|
| 67 |
+ Total uint64 |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+type Status struct {
|
|
| 71 |
+ PoolName string |
|
| 72 |
+ DataLoopback string |
|
| 73 |
+ MetadataLoopback string |
|
| 74 |
+ Data DiskUsage |
|
| 75 |
+ Metadata DiskUsage |
|
| 76 |
+ SectorSize uint64 |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+type DevStatus struct {
|
|
| 80 |
+ DeviceId int |
|
| 81 |
+ Size uint64 |
|
| 82 |
+ TransactionId uint64 |
|
| 83 |
+ SizeInSectors uint64 |
|
| 84 |
+ MappedSectors uint64 |
|
| 85 |
+ HighestMappedSector uint64 |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+type UnmountMode int |
|
| 89 |
+ |
|
| 90 |
+const ( |
|
| 91 |
+ UnmountRegular UnmountMode = iota |
|
| 92 |
+ UnmountFloat |
|
| 93 |
+ UnmountSink |
|
| 94 |
+) |
|
| 95 |
+ |
|
| 96 |
+func getDevName(name string) string {
|
|
| 97 |
+ return "/dev/mapper/" + name |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (info *DevInfo) Name() string {
|
|
| 101 |
+ hash := info.Hash |
|
| 102 |
+ if hash == "" {
|
|
| 103 |
+ hash = "base" |
|
| 104 |
+ } |
|
| 105 |
+ return fmt.Sprintf("%s-%s", info.devices.devicePrefix, hash)
|
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+func (info *DevInfo) DevName() string {
|
|
| 109 |
+ return getDevName(info.Name()) |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+func (devices *DeviceSet) loopbackDir() string {
|
|
| 113 |
+ return path.Join(devices.root, "devicemapper") |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+func (devices *DeviceSet) jsonFile() string {
|
|
| 117 |
+ return path.Join(devices.loopbackDir(), "json") |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+func (devices *DeviceSet) getPoolName() string {
|
|
| 121 |
+ return devices.devicePrefix + "-pool" |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+func (devices *DeviceSet) getPoolDevName() string {
|
|
| 125 |
+ return getDevName(devices.getPoolName()) |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+func (devices *DeviceSet) hasImage(name string) bool {
|
|
| 129 |
+ dirname := devices.loopbackDir() |
|
| 130 |
+ filename := path.Join(dirname, name) |
|
| 131 |
+ |
|
| 132 |
+ _, err := osStat(filename) |
|
| 133 |
+ return err == nil |
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+// ensureImage creates a sparse file of <size> bytes at the path |
|
| 137 |
+// <root>/devicemapper/<name>. |
|
| 138 |
+// If the file already exists, it does nothing. |
|
| 139 |
+// Either way it returns the full path. |
|
| 140 |
+func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
|
|
| 141 |
+ dirname := devices.loopbackDir() |
|
| 142 |
+ filename := path.Join(dirname, name) |
|
| 143 |
+ |
|
| 144 |
+ if err := osMkdirAll(dirname, 0700); err != nil && !osIsExist(err) {
|
|
| 145 |
+ return "", err |
|
| 146 |
+ } |
|
| 147 |
+ |
|
| 148 |
+ if _, err := osStat(filename); err != nil {
|
|
| 149 |
+ if !osIsNotExist(err) {
|
|
| 150 |
+ return "", err |
|
| 151 |
+ } |
|
| 152 |
+ utils.Debugf("Creating loopback file %s for device-manage use", filename)
|
|
| 153 |
+ file, err := osOpenFile(filename, osORdWr|osOCreate, 0600) |
|
| 154 |
+ if err != nil {
|
|
| 155 |
+ return "", err |
|
| 156 |
+ } |
|
| 157 |
+ defer file.Close() |
|
| 158 |
+ |
|
| 159 |
+ if err = file.Truncate(size); err != nil {
|
|
| 160 |
+ return "", err |
|
| 161 |
+ } |
|
| 162 |
+ } |
|
| 163 |
+ return filename, nil |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+func (devices *DeviceSet) allocateDeviceId() int {
|
|
| 167 |
+ // TODO: Add smarter reuse of deleted devices |
|
| 168 |
+ id := devices.nextFreeDevice |
|
| 169 |
+ devices.nextFreeDevice = devices.nextFreeDevice + 1 |
|
| 170 |
+ return id |
|
| 171 |
+} |
|
| 172 |
+ |
|
| 173 |
+func (devices *DeviceSet) allocateTransactionId() uint64 {
|
|
| 174 |
+ devices.NewTransactionId = devices.NewTransactionId + 1 |
|
| 175 |
+ return devices.NewTransactionId |
|
| 176 |
+} |
|
| 177 |
+ |
|
| 178 |
+func (devices *DeviceSet) saveMetadata() error {
|
|
| 179 |
+ jsonData, err := json.Marshal(devices.MetaData) |
|
| 180 |
+ if err != nil {
|
|
| 181 |
+ return fmt.Errorf("Error encoding metadata to json: %s", err)
|
|
| 182 |
+ } |
|
| 183 |
+ tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json") |
|
| 184 |
+ if err != nil {
|
|
| 185 |
+ return fmt.Errorf("Error creating metadata file: %s", err)
|
|
| 186 |
+ } |
|
| 187 |
+ |
|
| 188 |
+ n, err := tmpFile.Write(jsonData) |
|
| 189 |
+ if err != nil {
|
|
| 190 |
+ return fmt.Errorf("Error writing metadata to %s: %s", tmpFile.Name(), err)
|
|
| 191 |
+ } |
|
| 192 |
+ if n < len(jsonData) {
|
|
| 193 |
+ return io.ErrShortWrite |
|
| 194 |
+ } |
|
| 195 |
+ if err := tmpFile.Sync(); err != nil {
|
|
| 196 |
+ return fmt.Errorf("Error syncing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 197 |
+ } |
|
| 198 |
+ if err := tmpFile.Close(); err != nil {
|
|
| 199 |
+ return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 200 |
+ } |
|
| 201 |
+ if err := osRename(tmpFile.Name(), devices.jsonFile()); err != nil {
|
|
| 202 |
+ return fmt.Errorf("Error committing metadata file %s: %s", tmpFile.Name(), err)
|
|
| 203 |
+ } |
|
| 204 |
+ |
|
| 205 |
+ if devices.NewTransactionId != devices.TransactionId {
|
|
| 206 |
+ if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
|
|
| 207 |
+ return fmt.Errorf("Error setting devmapper transition ID: %s", err)
|
|
| 208 |
+ } |
|
| 209 |
+ devices.TransactionId = devices.NewTransactionId |
|
| 210 |
+ } |
|
| 211 |
+ return nil |
|
| 212 |
+} |
|
| 213 |
+ |
|
| 214 |
+func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) {
|
|
| 215 |
+ utils.Debugf("registerDevice(%v, %v)", id, hash)
|
|
| 216 |
+ info := &DevInfo{
|
|
| 217 |
+ Hash: hash, |
|
| 218 |
+ DeviceId: id, |
|
| 219 |
+ Size: size, |
|
| 220 |
+ TransactionId: devices.allocateTransactionId(), |
|
| 221 |
+ Initialized: false, |
|
| 222 |
+ devices: devices, |
|
| 223 |
+ } |
|
| 224 |
+ |
|
| 225 |
+ devices.Devices[hash] = info |
|
| 226 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 227 |
+ // Try to remove unused device |
|
| 228 |
+ delete(devices.Devices, hash) |
|
| 229 |
+ return nil, err |
|
| 230 |
+ } |
|
| 231 |
+ |
|
| 232 |
+ return info, nil |
|
| 233 |
+} |
|
| 234 |
+ |
|
| 235 |
+func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error {
|
|
| 236 |
+ utils.Debugf("activateDeviceIfNeeded(%v)", hash)
|
|
| 237 |
+ info := devices.Devices[hash] |
|
| 238 |
+ if info == nil {
|
|
| 239 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 240 |
+ } |
|
| 241 |
+ |
|
| 242 |
+ if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
|
|
| 243 |
+ return nil |
|
| 244 |
+ } |
|
| 245 |
+ |
|
| 246 |
+ return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size) |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
|
|
| 250 |
+ devname := info.DevName() |
|
| 251 |
+ |
|
| 252 |
+ err := execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname)
|
|
| 253 |
+ if err != nil {
|
|
| 254 |
+ err = execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname)
|
|
| 255 |
+ } |
|
| 256 |
+ if err != nil {
|
|
| 257 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 258 |
+ return err |
|
| 259 |
+ } |
|
| 260 |
+ return nil |
|
| 261 |
+} |
|
| 262 |
+ |
|
| 263 |
+func (devices *DeviceSet) loadMetaData() error {
|
|
| 264 |
+ utils.Debugf("loadMetadata()")
|
|
| 265 |
+ defer utils.Debugf("loadMetadata END")
|
|
| 266 |
+ _, _, _, params, err := getStatus(devices.getPoolName()) |
|
| 267 |
+ if err != nil {
|
|
| 268 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 269 |
+ return err |
|
| 270 |
+ } |
|
| 271 |
+ |
|
| 272 |
+ if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
|
|
| 273 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 274 |
+ return err |
|
| 275 |
+ } |
|
| 276 |
+ devices.NewTransactionId = devices.TransactionId |
|
| 277 |
+ |
|
| 278 |
+ jsonData, err := ioutil.ReadFile(devices.jsonFile()) |
|
| 279 |
+ if err != nil && !osIsNotExist(err) {
|
|
| 280 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 281 |
+ return err |
|
| 282 |
+ } |
|
| 283 |
+ |
|
| 284 |
+ devices.MetaData.Devices = make(map[string]*DevInfo) |
|
| 285 |
+ if jsonData != nil {
|
|
| 286 |
+ if err := json.Unmarshal(jsonData, &devices.MetaData); err != nil {
|
|
| 287 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 288 |
+ return err |
|
| 289 |
+ } |
|
| 290 |
+ } |
|
| 291 |
+ |
|
| 292 |
+ for hash, d := range devices.Devices {
|
|
| 293 |
+ d.Hash = hash |
|
| 294 |
+ d.devices = devices |
|
| 295 |
+ |
|
| 296 |
+ if d.DeviceId >= devices.nextFreeDevice {
|
|
| 297 |
+ devices.nextFreeDevice = d.DeviceId + 1 |
|
| 298 |
+ } |
|
| 299 |
+ |
|
| 300 |
+ // If the transaction id is larger than the actual one we lost the device due to some crash |
|
| 301 |
+ if d.TransactionId > devices.TransactionId {
|
|
| 302 |
+ utils.Debugf("Removing lost device %s with id %d", hash, d.TransactionId)
|
|
| 303 |
+ delete(devices.Devices, hash) |
|
| 304 |
+ } |
|
| 305 |
+ } |
|
| 306 |
+ return nil |
|
| 307 |
+} |
|
| 308 |
+ |
|
| 309 |
+func (devices *DeviceSet) setupBaseImage() error {
|
|
| 310 |
+ oldInfo := devices.Devices[""] |
|
| 311 |
+ if oldInfo != nil && oldInfo.Initialized {
|
|
| 312 |
+ return nil |
|
| 313 |
+ } |
|
| 314 |
+ |
|
| 315 |
+ if oldInfo != nil && !oldInfo.Initialized {
|
|
| 316 |
+ utils.Debugf("Removing uninitialized base image")
|
|
| 317 |
+ if err := devices.deleteDevice(""); err != nil {
|
|
| 318 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 319 |
+ return err |
|
| 320 |
+ } |
|
| 321 |
+ } |
|
| 322 |
+ |
|
| 323 |
+ utils.Debugf("Initializing base device-manager snapshot")
|
|
| 324 |
+ |
|
| 325 |
+ id := devices.allocateDeviceId() |
|
| 326 |
+ |
|
| 327 |
+ // Create initial device |
|
| 328 |
+ if err := createDevice(devices.getPoolDevName(), id); err != nil {
|
|
| 329 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 330 |
+ return err |
|
| 331 |
+ } |
|
| 332 |
+ |
|
| 333 |
+ utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
|
|
| 334 |
+ info, err := devices.registerDevice(id, "", DefaultBaseFsSize) |
|
| 335 |
+ if err != nil {
|
|
| 336 |
+ _ = deleteDevice(devices.getPoolDevName(), id) |
|
| 337 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 338 |
+ return err |
|
| 339 |
+ } |
|
| 340 |
+ |
|
| 341 |
+ utils.Debugf("Creating filesystem on base device-manager snapshot")
|
|
| 342 |
+ |
|
| 343 |
+ if err = devices.activateDeviceIfNeeded(""); err != nil {
|
|
| 344 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 345 |
+ return err |
|
| 346 |
+ } |
|
| 347 |
+ |
|
| 348 |
+ if err := devices.createFilesystem(info); err != nil {
|
|
| 349 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 350 |
+ return err |
|
| 351 |
+ } |
|
| 352 |
+ |
|
| 353 |
+ info.Initialized = true |
|
| 354 |
+ if err = devices.saveMetadata(); err != nil {
|
|
| 355 |
+ info.Initialized = false |
|
| 356 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 357 |
+ return err |
|
| 358 |
+ } |
|
| 359 |
+ |
|
| 360 |
+ return nil |
|
| 361 |
+} |
|
| 362 |
+ |
|
| 363 |
+func setCloseOnExec(name string) {
|
|
| 364 |
+ if fileInfos, _ := ioutil.ReadDir("/proc/self/fd"); fileInfos != nil {
|
|
| 365 |
+ for _, i := range fileInfos {
|
|
| 366 |
+ link, _ := osReadlink(filepath.Join("/proc/self/fd", i.Name()))
|
|
| 367 |
+ if link == name {
|
|
| 368 |
+ fd, err := strconv.Atoi(i.Name()) |
|
| 369 |
+ if err == nil {
|
|
| 370 |
+ sysCloseOnExec(fd) |
|
| 371 |
+ } |
|
| 372 |
+ } |
|
| 373 |
+ } |
|
| 374 |
+ } |
|
| 375 |
+} |
|
| 376 |
+ |
|
| 377 |
+func (devices *DeviceSet) log(level int, file string, line int, dmError int, message string) {
|
|
| 378 |
+ if level >= 7 {
|
|
| 379 |
+ return // Ignore _LOG_DEBUG |
|
| 380 |
+ } |
|
| 381 |
+ |
|
| 382 |
+ if strings.Contains(message, "busy") {
|
|
| 383 |
+ devices.sawBusy = true |
|
| 384 |
+ } |
|
| 385 |
+ |
|
| 386 |
+ utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 387 |
+} |
|
| 388 |
+ |
|
| 389 |
+func major(device uint64) uint64 {
|
|
| 390 |
+ return (device >> 8) & 0xfff |
|
| 391 |
+} |
|
| 392 |
+ |
|
| 393 |
+func minor(device uint64) uint64 {
|
|
| 394 |
+ return (device & 0xff) | ((device >> 12) & 0xfff00) |
|
| 395 |
+} |
|
| 396 |
+ |
|
| 397 |
+func (devices *DeviceSet) ResizePool(size int64) error {
|
|
| 398 |
+ dirname := devices.loopbackDir() |
|
| 399 |
+ datafilename := path.Join(dirname, "data") |
|
| 400 |
+ metadatafilename := path.Join(dirname, "metadata") |
|
| 401 |
+ |
|
| 402 |
+ datafile, err := osOpenFile(datafilename, osORdWr, 0) |
|
| 403 |
+ if datafile == nil {
|
|
| 404 |
+ return err |
|
| 405 |
+ } |
|
| 406 |
+ defer datafile.Close() |
|
| 407 |
+ |
|
| 408 |
+ fi, err := datafile.Stat() |
|
| 409 |
+ if fi == nil {
|
|
| 410 |
+ return err |
|
| 411 |
+ } |
|
| 412 |
+ |
|
| 413 |
+ if fi.Size() > size {
|
|
| 414 |
+ return fmt.Errorf("Can't shrink file")
|
|
| 415 |
+ } |
|
| 416 |
+ |
|
| 417 |
+ dataloopback := FindLoopDeviceFor(datafile) |
|
| 418 |
+ if dataloopback == nil {
|
|
| 419 |
+ return fmt.Errorf("Unable to find loopback mount for: %s", datafilename)
|
|
| 420 |
+ } |
|
| 421 |
+ defer dataloopback.Close() |
|
| 422 |
+ |
|
| 423 |
+ metadatafile, err := osOpenFile(metadatafilename, osORdWr, 0) |
|
| 424 |
+ if metadatafile == nil {
|
|
| 425 |
+ return err |
|
| 426 |
+ } |
|
| 427 |
+ defer metadatafile.Close() |
|
| 428 |
+ |
|
| 429 |
+ metadataloopback := FindLoopDeviceFor(metadatafile) |
|
| 430 |
+ if metadataloopback == nil {
|
|
| 431 |
+ return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename)
|
|
| 432 |
+ } |
|
| 433 |
+ defer metadataloopback.Close() |
|
| 434 |
+ |
|
| 435 |
+ // Grow loopback file |
|
| 436 |
+ if err := datafile.Truncate(size); err != nil {
|
|
| 437 |
+ return fmt.Errorf("Unable to grow loopback file: %s", err)
|
|
| 438 |
+ } |
|
| 439 |
+ |
|
| 440 |
+ // Reload size for loopback device |
|
| 441 |
+ if err := LoopbackSetCapacity(dataloopback); err != nil {
|
|
| 442 |
+ return fmt.Errorf("Unable to update loopback capacity: %s", err)
|
|
| 443 |
+ } |
|
| 444 |
+ |
|
| 445 |
+ // Suspend the pool |
|
| 446 |
+ if err := suspendDevice(devices.getPoolName()); err != nil {
|
|
| 447 |
+ return fmt.Errorf("Unable to suspend pool: %s", err)
|
|
| 448 |
+ } |
|
| 449 |
+ |
|
| 450 |
+ // Reload with the new block sizes |
|
| 451 |
+ if err := reloadPool(devices.getPoolName(), dataloopback, metadataloopback); err != nil {
|
|
| 452 |
+ return fmt.Errorf("Unable to reload pool: %s", err)
|
|
| 453 |
+ } |
|
| 454 |
+ |
|
| 455 |
+ // Resume the pool |
|
| 456 |
+ if err := resumeDevice(devices.getPoolName()); err != nil {
|
|
| 457 |
+ return fmt.Errorf("Unable to resume pool: %s", err)
|
|
| 458 |
+ } |
|
| 459 |
+ |
|
| 460 |
+ return nil |
|
| 461 |
+} |
|
| 462 |
+ |
|
| 463 |
+func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
|
| 464 |
+ logInit(devices) |
|
| 465 |
+ |
|
| 466 |
+ // Make sure the sparse images exist in <root>/devicemapper/data and |
|
| 467 |
+ // <root>/devicemapper/metadata |
|
| 468 |
+ |
|
| 469 |
+ hasData := devices.hasImage("data")
|
|
| 470 |
+ hasMetadata := devices.hasImage("metadata")
|
|
| 471 |
+ |
|
| 472 |
+ if !doInit && !hasData {
|
|
| 473 |
+ return errors.New("Loopback data file not found")
|
|
| 474 |
+ } |
|
| 475 |
+ |
|
| 476 |
+ if !doInit && !hasMetadata {
|
|
| 477 |
+ return errors.New("Loopback metadata file not found")
|
|
| 478 |
+ } |
|
| 479 |
+ |
|
| 480 |
+ createdLoopback := !hasData || !hasMetadata |
|
| 481 |
+ data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
|
|
| 482 |
+ if err != nil {
|
|
| 483 |
+ utils.Debugf("Error device ensureImage (data): %s\n", err)
|
|
| 484 |
+ return err |
|
| 485 |
+ } |
|
| 486 |
+ metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
|
|
| 487 |
+ if err != nil {
|
|
| 488 |
+ utils.Debugf("Error device ensureImage (metadata): %s\n", err)
|
|
| 489 |
+ return err |
|
| 490 |
+ } |
|
| 491 |
+ |
|
| 492 |
+ // Set the device prefix from the device id and inode of the docker root dir |
|
| 493 |
+ |
|
| 494 |
+ st, err := osStat(devices.root) |
|
| 495 |
+ if err != nil {
|
|
| 496 |
+ return fmt.Errorf("Error looking up dir %s: %s", devices.root, err)
|
|
| 497 |
+ } |
|
| 498 |
+ sysSt := toSysStatT(st.Sys()) |
|
| 499 |
+ // "reg-" stands for "regular file". |
|
| 500 |
+ // In the future we might use "dev-" for "device file", etc. |
|
| 501 |
+ // docker-maj,min[-inode] stands for: |
|
| 502 |
+ // - Managed by docker |
|
| 503 |
+ // - The target of this device is at major <maj> and minor <min> |
|
| 504 |
+ // - If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself. |
|
| 505 |
+ devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
|
|
| 506 |
+ utils.Debugf("Generated prefix: %s", devices.devicePrefix)
|
|
| 507 |
+ |
|
| 508 |
+ // Check for the existence of the device <prefix>-pool |
|
| 509 |
+ utils.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
|
|
| 510 |
+ info, err := getInfo(devices.getPoolName()) |
|
| 511 |
+ if info == nil {
|
|
| 512 |
+ utils.Debugf("Error device getInfo: %s", err)
|
|
| 513 |
+ return err |
|
| 514 |
+ } |
|
| 515 |
+ |
|
| 516 |
+ // It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files |
|
| 517 |
+ // that are not Close-on-exec, and lxc-start will die if it inherits any unexpected files, |
|
| 518 |
+ // so we add this badhack to make sure it closes itself |
|
| 519 |
+ setCloseOnExec("/dev/mapper/control")
|
|
| 520 |
+ |
|
| 521 |
+ // If the pool doesn't exist, create it |
|
| 522 |
+ if info.Exists == 0 {
|
|
| 523 |
+ utils.Debugf("Pool doesn't exist. Creating it.")
|
|
| 524 |
+ |
|
| 525 |
+ dataFile, err := attachLoopDevice(data) |
|
| 526 |
+ if err != nil {
|
|
| 527 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 528 |
+ return err |
|
| 529 |
+ } |
|
| 530 |
+ defer dataFile.Close() |
|
| 531 |
+ |
|
| 532 |
+ metadataFile, err := attachLoopDevice(metadata) |
|
| 533 |
+ if err != nil {
|
|
| 534 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 535 |
+ return err |
|
| 536 |
+ } |
|
| 537 |
+ defer metadataFile.Close() |
|
| 538 |
+ |
|
| 539 |
+ if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
|
|
| 540 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 541 |
+ return err |
|
| 542 |
+ } |
|
| 543 |
+ } |
|
| 544 |
+ |
|
| 545 |
+ // If we didn't just create the data or metadata image, we need to |
|
| 546 |
+ // load the metadata from the existing file. |
|
| 547 |
+ if !createdLoopback {
|
|
| 548 |
+ if err = devices.loadMetaData(); err != nil {
|
|
| 549 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 550 |
+ return err |
|
| 551 |
+ } |
|
| 552 |
+ } |
|
| 553 |
+ |
|
| 554 |
+ // Setup the base image |
|
| 555 |
+ if doInit {
|
|
| 556 |
+ if err := devices.setupBaseImage(); err != nil {
|
|
| 557 |
+ utils.Debugf("Error device setupBaseImage: %s\n", err)
|
|
| 558 |
+ return err |
|
| 559 |
+ } |
|
| 560 |
+ } |
|
| 561 |
+ |
|
| 562 |
+ return nil |
|
| 563 |
+} |
|
| 564 |
+ |
|
| 565 |
+func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
|
|
| 566 |
+ devices.Lock() |
|
| 567 |
+ defer devices.Unlock() |
|
| 568 |
+ |
|
| 569 |
+ if devices.Devices[hash] != nil {
|
|
| 570 |
+ return fmt.Errorf("hash %s already exists", hash)
|
|
| 571 |
+ } |
|
| 572 |
+ |
|
| 573 |
+ baseInfo := devices.Devices[baseHash] |
|
| 574 |
+ if baseInfo == nil {
|
|
| 575 |
+ return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
|
|
| 576 |
+ } |
|
| 577 |
+ |
|
| 578 |
+ baseInfo.lock.Lock() |
|
| 579 |
+ defer baseInfo.lock.Unlock() |
|
| 580 |
+ |
|
| 581 |
+ deviceId := devices.allocateDeviceId() |
|
| 582 |
+ |
|
| 583 |
+ if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
|
|
| 584 |
+ utils.Debugf("Error creating snap device: %s\n", err)
|
|
| 585 |
+ return err |
|
| 586 |
+ } |
|
| 587 |
+ |
|
| 588 |
+ if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
|
|
| 589 |
+ deleteDevice(devices.getPoolDevName(), deviceId) |
|
| 590 |
+ utils.Debugf("Error registering device: %s\n", err)
|
|
| 591 |
+ return err |
|
| 592 |
+ } |
|
| 593 |
+ return nil |
|
| 594 |
+} |
|
| 595 |
+ |
|
| 596 |
+func (devices *DeviceSet) deleteDevice(hash string) error {
|
|
| 597 |
+ info := devices.Devices[hash] |
|
| 598 |
+ if info == nil {
|
|
| 599 |
+ return fmt.Errorf("hash %s doesn't exists", hash)
|
|
| 600 |
+ } |
|
| 601 |
+ |
|
| 602 |
+ // This is a workaround for the kernel not discarding block so |
|
| 603 |
+ // on the thin pool when we remove a thinp device, so we do it |
|
| 604 |
+ // manually |
|
| 605 |
+ if err := devices.activateDeviceIfNeeded(hash); err == nil {
|
|
| 606 |
+ if err := BlockDeviceDiscard(info.DevName()); err != nil {
|
|
| 607 |
+ utils.Debugf("Error discarding block on device: %s (ignoring)\n", err)
|
|
| 608 |
+ } |
|
| 609 |
+ } |
|
| 610 |
+ |
|
| 611 |
+ devinfo, _ := getInfo(info.Name()) |
|
| 612 |
+ if devinfo != nil && devinfo.Exists != 0 {
|
|
| 613 |
+ if err := devices.removeDeviceAndWait(info.Name()); err != nil {
|
|
| 614 |
+ utils.Debugf("Error removing device: %s\n", err)
|
|
| 615 |
+ return err |
|
| 616 |
+ } |
|
| 617 |
+ } |
|
| 618 |
+ |
|
| 619 |
+ if info.Initialized {
|
|
| 620 |
+ info.Initialized = false |
|
| 621 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 622 |
+ utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 623 |
+ return err |
|
| 624 |
+ } |
|
| 625 |
+ } |
|
| 626 |
+ |
|
| 627 |
+ if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
|
|
| 628 |
+ utils.Debugf("Error deleting device: %s\n", err)
|
|
| 629 |
+ return err |
|
| 630 |
+ } |
|
| 631 |
+ |
|
| 632 |
+ devices.allocateTransactionId() |
|
| 633 |
+ delete(devices.Devices, info.Hash) |
|
| 634 |
+ |
|
| 635 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 636 |
+ devices.Devices[info.Hash] = info |
|
| 637 |
+ utils.Debugf("Error saving meta data: %s\n", err)
|
|
| 638 |
+ return err |
|
| 639 |
+ } |
|
| 640 |
+ |
|
| 641 |
+ return nil |
|
| 642 |
+} |
|
| 643 |
+ |
|
| 644 |
+func (devices *DeviceSet) DeleteDevice(hash string) error {
|
|
| 645 |
+ devices.Lock() |
|
| 646 |
+ defer devices.Unlock() |
|
| 647 |
+ |
|
| 648 |
+ info := devices.Devices[hash] |
|
| 649 |
+ if info == nil {
|
|
| 650 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 651 |
+ } |
|
| 652 |
+ |
|
| 653 |
+ info.lock.Lock() |
|
| 654 |
+ defer info.lock.Unlock() |
|
| 655 |
+ |
|
| 656 |
+ return devices.deleteDevice(hash) |
|
| 657 |
+} |
|
| 658 |
+ |
|
| 659 |
+func (devices *DeviceSet) deactivatePool() error {
|
|
| 660 |
+ utils.Debugf("[devmapper] deactivatePool()")
|
|
| 661 |
+ defer utils.Debugf("[devmapper] deactivatePool END")
|
|
| 662 |
+ devname := devices.getPoolDevName() |
|
| 663 |
+ devinfo, err := getInfo(devname) |
|
| 664 |
+ if err != nil {
|
|
| 665 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 666 |
+ return err |
|
| 667 |
+ } |
|
| 668 |
+ if devinfo.Exists != 0 {
|
|
| 669 |
+ return removeDevice(devname) |
|
| 670 |
+ } |
|
| 671 |
+ |
|
| 672 |
+ return nil |
|
| 673 |
+} |
|
| 674 |
+ |
|
| 675 |
+func (devices *DeviceSet) deactivateDevice(hash string) error {
|
|
| 676 |
+ utils.Debugf("[devmapper] deactivateDevice(%s)", hash)
|
|
| 677 |
+ defer utils.Debugf("[devmapper] deactivateDevice END")
|
|
| 678 |
+ |
|
| 679 |
+ info := devices.Devices[hash] |
|
| 680 |
+ if info == nil {
|
|
| 681 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 682 |
+ } |
|
| 683 |
+ devinfo, err := getInfo(info.Name()) |
|
| 684 |
+ if err != nil {
|
|
| 685 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 686 |
+ return err |
|
| 687 |
+ } |
|
| 688 |
+ if devinfo.Exists != 0 {
|
|
| 689 |
+ if err := devices.removeDeviceAndWait(info.Name()); err != nil {
|
|
| 690 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 691 |
+ return err |
|
| 692 |
+ } |
|
| 693 |
+ } |
|
| 694 |
+ |
|
| 695 |
+ return nil |
|
| 696 |
+} |
|
| 697 |
+ |
|
| 698 |
+// Issues the underlying dm remove operation and then waits |
|
| 699 |
+// for it to finish. |
|
| 700 |
+func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
|
|
| 701 |
+ var err error |
|
| 702 |
+ |
|
| 703 |
+ for i := 0; i < 1000; i++ {
|
|
| 704 |
+ devices.sawBusy = false |
|
| 705 |
+ err = removeDevice(devname) |
|
| 706 |
+ if err == nil {
|
|
| 707 |
+ break |
|
| 708 |
+ } |
|
| 709 |
+ if !devices.sawBusy {
|
|
| 710 |
+ return err |
|
| 711 |
+ } |
|
| 712 |
+ |
|
| 713 |
+ // If we see EBUSY it may be a transient error, |
|
| 714 |
+ // sleep a bit a retry a few times. |
|
| 715 |
+ devices.Unlock() |
|
| 716 |
+ time.Sleep(10 * time.Millisecond) |
|
| 717 |
+ devices.Lock() |
|
| 718 |
+ } |
|
| 719 |
+ if err != nil {
|
|
| 720 |
+ return err |
|
| 721 |
+ } |
|
| 722 |
+ |
|
| 723 |
+ if err := devices.waitRemove(devname); err != nil {
|
|
| 724 |
+ return err |
|
| 725 |
+ } |
|
| 726 |
+ return nil |
|
| 727 |
+} |
|
| 728 |
+ |
|
| 729 |
+// waitRemove blocks until either: |
|
| 730 |
+// a) the device registered at <device_set_prefix>-<hash> is removed, |
|
| 731 |
+// or b) the 1 second timeout expires. |
|
| 732 |
+func (devices *DeviceSet) waitRemove(devname string) error {
|
|
| 733 |
+ utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname)
|
|
| 734 |
+ defer utils.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname)
|
|
| 735 |
+ i := 0 |
|
| 736 |
+ for ; i < 1000; i += 1 {
|
|
| 737 |
+ devinfo, err := getInfo(devname) |
|
| 738 |
+ if err != nil {
|
|
| 739 |
+ // If there is an error we assume the device doesn't exist. |
|
| 740 |
+ // The error might actually be something else, but we can't differentiate. |
|
| 741 |
+ return nil |
|
| 742 |
+ } |
|
| 743 |
+ if i%100 == 0 {
|
|
| 744 |
+ utils.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
|
|
| 745 |
+ } |
|
| 746 |
+ if devinfo.Exists == 0 {
|
|
| 747 |
+ break |
|
| 748 |
+ } |
|
| 749 |
+ |
|
| 750 |
+ devices.Unlock() |
|
| 751 |
+ time.Sleep(10 * time.Millisecond) |
|
| 752 |
+ devices.Lock() |
|
| 753 |
+ } |
|
| 754 |
+ if i == 1000 {
|
|
| 755 |
+ return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
|
|
| 756 |
+ } |
|
| 757 |
+ return nil |
|
| 758 |
+} |
|
| 759 |
+ |
|
| 760 |
+// waitClose blocks until either: |
|
| 761 |
+// a) the device registered at <device_set_prefix>-<hash> is closed, |
|
| 762 |
+// or b) the 1 second timeout expires. |
|
| 763 |
+func (devices *DeviceSet) waitClose(hash string) error {
|
|
| 764 |
+ info := devices.Devices[hash] |
|
| 765 |
+ if info == nil {
|
|
| 766 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 767 |
+ } |
|
| 768 |
+ i := 0 |
|
| 769 |
+ for ; i < 1000; i += 1 {
|
|
| 770 |
+ devinfo, err := getInfo(info.Name()) |
|
| 771 |
+ if err != nil {
|
|
| 772 |
+ return err |
|
| 773 |
+ } |
|
| 774 |
+ if i%100 == 0 {
|
|
| 775 |
+ utils.Debugf("Waiting for unmount of %s: opencount=%d", hash, devinfo.OpenCount)
|
|
| 776 |
+ } |
|
| 777 |
+ if devinfo.OpenCount == 0 {
|
|
| 778 |
+ break |
|
| 779 |
+ } |
|
| 780 |
+ time.Sleep(1 * time.Millisecond) |
|
| 781 |
+ } |
|
| 782 |
+ if i == 1000 {
|
|
| 783 |
+ return fmt.Errorf("Timeout while waiting for device %s to close", hash)
|
|
| 784 |
+ } |
|
| 785 |
+ return nil |
|
| 786 |
+} |
|
| 787 |
+ |
|
| 788 |
+func (devices *DeviceSet) Shutdown() error {
|
|
| 789 |
+ devices.Lock() |
|
| 790 |
+ defer devices.Unlock() |
|
| 791 |
+ |
|
| 792 |
+ utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
|
|
| 793 |
+ utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
|
|
| 794 |
+ defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
|
|
| 795 |
+ |
|
| 796 |
+ for _, info := range devices.Devices {
|
|
| 797 |
+ info.lock.Lock() |
|
| 798 |
+ if info.mountCount > 0 {
|
|
| 799 |
+ if err := sysUnmount(info.mountPath, 0); err != nil {
|
|
| 800 |
+ utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
|
|
| 801 |
+ } |
|
| 802 |
+ } |
|
| 803 |
+ info.lock.Unlock() |
|
| 804 |
+ } |
|
| 805 |
+ |
|
| 806 |
+ for _, d := range devices.Devices {
|
|
| 807 |
+ d.lock.Lock() |
|
| 808 |
+ |
|
| 809 |
+ if err := devices.waitClose(d.Hash); err != nil {
|
|
| 810 |
+ utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
|
|
| 811 |
+ } |
|
| 812 |
+ if err := devices.deactivateDevice(d.Hash); err != nil {
|
|
| 813 |
+ utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
|
|
| 814 |
+ } |
|
| 815 |
+ |
|
| 816 |
+ d.lock.Unlock() |
|
| 817 |
+ } |
|
| 818 |
+ |
|
| 819 |
+ if err := devices.deactivatePool(); err != nil {
|
|
| 820 |
+ utils.Debugf("Shutdown deactivate pool , error: %s\n", err)
|
|
| 821 |
+ } |
|
| 822 |
+ |
|
| 823 |
+ return nil |
|
| 824 |
+} |
|
| 825 |
+ |
|
| 826 |
+func (devices *DeviceSet) MountDevice(hash, path string) error {
|
|
| 827 |
+ devices.Lock() |
|
| 828 |
+ defer devices.Unlock() |
|
| 829 |
+ |
|
| 830 |
+ info := devices.Devices[hash] |
|
| 831 |
+ if info == nil {
|
|
| 832 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 833 |
+ } |
|
| 834 |
+ |
|
| 835 |
+ info.lock.Lock() |
|
| 836 |
+ defer info.lock.Unlock() |
|
| 837 |
+ |
|
| 838 |
+ if info.mountCount > 0 {
|
|
| 839 |
+ if path != info.mountPath {
|
|
| 840 |
+ return fmt.Errorf("Trying to mount devmapper device in multple places (%s, %s)", info.mountPath, path)
|
|
| 841 |
+ } |
|
| 842 |
+ |
|
| 843 |
+ if info.floating {
|
|
| 844 |
+ // Steal floating ref |
|
| 845 |
+ info.floating = false |
|
| 846 |
+ } else {
|
|
| 847 |
+ info.mountCount++ |
|
| 848 |
+ } |
|
| 849 |
+ return nil |
|
| 850 |
+ } |
|
| 851 |
+ |
|
| 852 |
+ if err := devices.activateDeviceIfNeeded(hash); err != nil {
|
|
| 853 |
+ return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
|
|
| 854 |
+ } |
|
| 855 |
+ |
|
| 856 |
+ var flags uintptr = sysMsMgcVal |
|
| 857 |
+ |
|
| 858 |
+ err := sysMount(info.DevName(), path, "ext4", flags, "discard") |
|
| 859 |
+ if err != nil && err == sysEInval {
|
|
| 860 |
+ err = sysMount(info.DevName(), path, "ext4", flags, "") |
|
| 861 |
+ } |
|
| 862 |
+ if err != nil {
|
|
| 863 |
+ return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
|
|
| 864 |
+ } |
|
| 865 |
+ |
|
| 866 |
+ info.mountCount = 1 |
|
| 867 |
+ info.mountPath = path |
|
| 868 |
+ info.floating = false |
|
| 869 |
+ |
|
| 870 |
+ return devices.setInitialized(hash) |
|
| 871 |
+} |
|
| 872 |
+ |
|
| 873 |
+func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error {
|
|
| 874 |
+ utils.Debugf("[devmapper] UnmountDevice(hash=%s, mode=%d)", hash, mode)
|
|
| 875 |
+ defer utils.Debugf("[devmapper] UnmountDevice END")
|
|
| 876 |
+ devices.Lock() |
|
| 877 |
+ defer devices.Unlock() |
|
| 878 |
+ |
|
| 879 |
+ info := devices.Devices[hash] |
|
| 880 |
+ if info == nil {
|
|
| 881 |
+ return fmt.Errorf("UnmountDevice: no such device %s\n", hash)
|
|
| 882 |
+ } |
|
| 883 |
+ |
|
| 884 |
+ info.lock.Lock() |
|
| 885 |
+ defer info.lock.Unlock() |
|
| 886 |
+ |
|
| 887 |
+ if mode == UnmountFloat {
|
|
| 888 |
+ if info.floating {
|
|
| 889 |
+ return fmt.Errorf("UnmountDevice: can't float floating reference %s\n", hash)
|
|
| 890 |
+ } |
|
| 891 |
+ |
|
| 892 |
+ // Leave this reference floating |
|
| 893 |
+ info.floating = true |
|
| 894 |
+ return nil |
|
| 895 |
+ } |
|
| 896 |
+ |
|
| 897 |
+ if mode == UnmountSink {
|
|
| 898 |
+ if !info.floating {
|
|
| 899 |
+ // Someone already sunk this |
|
| 900 |
+ return nil |
|
| 901 |
+ } |
|
| 902 |
+ // Otherwise, treat this as a regular unmount |
|
| 903 |
+ } |
|
| 904 |
+ |
|
| 905 |
+ if info.mountCount == 0 {
|
|
| 906 |
+ return fmt.Errorf("UnmountDevice: device not-mounted id %s\n", hash)
|
|
| 907 |
+ } |
|
| 908 |
+ |
|
| 909 |
+ info.mountCount-- |
|
| 910 |
+ if info.mountCount > 0 {
|
|
| 911 |
+ return nil |
|
| 912 |
+ } |
|
| 913 |
+ |
|
| 914 |
+ utils.Debugf("[devmapper] Unmount(%s)", info.mountPath)
|
|
| 915 |
+ if err := sysUnmount(info.mountPath, 0); err != nil {
|
|
| 916 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 917 |
+ return err |
|
| 918 |
+ } |
|
| 919 |
+ utils.Debugf("[devmapper] Unmount done")
|
|
| 920 |
+ // Wait for the unmount to be effective, |
|
| 921 |
+ // by watching the value of Info.OpenCount for the device |
|
| 922 |
+ if err := devices.waitClose(hash); err != nil {
|
|
| 923 |
+ return err |
|
| 924 |
+ } |
|
| 925 |
+ |
|
| 926 |
+ devices.deactivateDevice(hash) |
|
| 927 |
+ |
|
| 928 |
+ info.mountPath = "" |
|
| 929 |
+ |
|
| 930 |
+ return nil |
|
| 931 |
+} |
|
| 932 |
+ |
|
| 933 |
+func (devices *DeviceSet) HasDevice(hash string) bool {
|
|
| 934 |
+ devices.Lock() |
|
| 935 |
+ defer devices.Unlock() |
|
| 936 |
+ |
|
| 937 |
+ return devices.Devices[hash] != nil |
|
| 938 |
+} |
|
| 939 |
+ |
|
| 940 |
+func (devices *DeviceSet) HasInitializedDevice(hash string) bool {
|
|
| 941 |
+ devices.Lock() |
|
| 942 |
+ defer devices.Unlock() |
|
| 943 |
+ |
|
| 944 |
+ info := devices.Devices[hash] |
|
| 945 |
+ return info != nil && info.Initialized |
|
| 946 |
+} |
|
| 947 |
+ |
|
| 948 |
+func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
|
|
| 949 |
+ devices.Lock() |
|
| 950 |
+ defer devices.Unlock() |
|
| 951 |
+ |
|
| 952 |
+ info := devices.Devices[hash] |
|
| 953 |
+ if info == nil {
|
|
| 954 |
+ return false |
|
| 955 |
+ } |
|
| 956 |
+ |
|
| 957 |
+ info.lock.Lock() |
|
| 958 |
+ defer info.lock.Unlock() |
|
| 959 |
+ |
|
| 960 |
+ devinfo, _ := getInfo(info.Name()) |
|
| 961 |
+ return devinfo != nil && devinfo.Exists != 0 |
|
| 962 |
+} |
|
| 963 |
+ |
|
| 964 |
+func (devices *DeviceSet) setInitialized(hash string) error {
|
|
| 965 |
+ info := devices.Devices[hash] |
|
| 966 |
+ if info == nil {
|
|
| 967 |
+ return fmt.Errorf("Unknown device %s", hash)
|
|
| 968 |
+ } |
|
| 969 |
+ |
|
| 970 |
+ info.Initialized = true |
|
| 971 |
+ if err := devices.saveMetadata(); err != nil {
|
|
| 972 |
+ info.Initialized = false |
|
| 973 |
+ utils.Debugf("\n--->Err: %s\n", err)
|
|
| 974 |
+ return err |
|
| 975 |
+ } |
|
| 976 |
+ |
|
| 977 |
+ return nil |
|
| 978 |
+} |
|
| 979 |
+ |
|
| 980 |
+func (devices *DeviceSet) List() []string {
|
|
| 981 |
+ devices.Lock() |
|
| 982 |
+ defer devices.Unlock() |
|
| 983 |
+ |
|
| 984 |
+ ids := make([]string, len(devices.Devices)) |
|
| 985 |
+ i := 0 |
|
| 986 |
+ for k := range devices.Devices {
|
|
| 987 |
+ ids[i] = k |
|
| 988 |
+ i++ |
|
| 989 |
+ } |
|
| 990 |
+ return ids |
|
| 991 |
+} |
|
| 992 |
+ |
|
| 993 |
+func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) {
|
|
| 994 |
+ var params string |
|
| 995 |
+ _, sizeInSectors, _, params, err = getStatus(devName) |
|
| 996 |
+ if err != nil {
|
|
| 997 |
+ return |
|
| 998 |
+ } |
|
| 999 |
+ if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err == nil {
|
|
| 1000 |
+ return |
|
| 1001 |
+ } |
|
| 1002 |
+ return |
|
| 1003 |
+} |
|
| 1004 |
+ |
|
| 1005 |
+func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) {
|
|
| 1006 |
+ devices.Lock() |
|
| 1007 |
+ defer devices.Unlock() |
|
| 1008 |
+ |
|
| 1009 |
+ info := devices.Devices[hash] |
|
| 1010 |
+ if info == nil {
|
|
| 1011 |
+ return nil, fmt.Errorf("No device %s", hash)
|
|
| 1012 |
+ } |
|
| 1013 |
+ |
|
| 1014 |
+ info.lock.Lock() |
|
| 1015 |
+ defer info.lock.Unlock() |
|
| 1016 |
+ |
|
| 1017 |
+ status := &DevStatus{
|
|
| 1018 |
+ DeviceId: info.DeviceId, |
|
| 1019 |
+ Size: info.Size, |
|
| 1020 |
+ TransactionId: info.TransactionId, |
|
| 1021 |
+ } |
|
| 1022 |
+ |
|
| 1023 |
+ if err := devices.activateDeviceIfNeeded(hash); err != nil {
|
|
| 1024 |
+ return nil, fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
|
|
| 1025 |
+ } |
|
| 1026 |
+ |
|
| 1027 |
+ if sizeInSectors, mappedSectors, highestMappedSector, err := devices.deviceStatus(info.DevName()); err != nil {
|
|
| 1028 |
+ return nil, err |
|
| 1029 |
+ } else {
|
|
| 1030 |
+ status.SizeInSectors = sizeInSectors |
|
| 1031 |
+ status.MappedSectors = mappedSectors |
|
| 1032 |
+ status.HighestMappedSector = highestMappedSector |
|
| 1033 |
+ } |
|
| 1034 |
+ |
|
| 1035 |
+ return status, nil |
|
| 1036 |
+} |
|
| 1037 |
+ |
|
| 1038 |
+func (devices *DeviceSet) poolStatus() (totalSizeInSectors, transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64, err error) {
|
|
| 1039 |
+ var params string |
|
| 1040 |
+ if _, totalSizeInSectors, _, params, err = getStatus(devices.getPoolName()); err == nil {
|
|
| 1041 |
+ _, err = fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal) |
|
| 1042 |
+ } |
|
| 1043 |
+ return |
|
| 1044 |
+} |
|
| 1045 |
+ |
|
| 1046 |
+func (devices *DeviceSet) Status() *Status {
|
|
| 1047 |
+ devices.Lock() |
|
| 1048 |
+ defer devices.Unlock() |
|
| 1049 |
+ |
|
| 1050 |
+ status := &Status{}
|
|
| 1051 |
+ |
|
| 1052 |
+ status.PoolName = devices.getPoolName() |
|
| 1053 |
+ status.DataLoopback = path.Join(devices.loopbackDir(), "data") |
|
| 1054 |
+ status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") |
|
| 1055 |
+ |
|
| 1056 |
+ totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() |
|
| 1057 |
+ if err == nil {
|
|
| 1058 |
+ // Convert from blocks to bytes |
|
| 1059 |
+ blockSizeInSectors := totalSizeInSectors / dataTotal |
|
| 1060 |
+ |
|
| 1061 |
+ status.Data.Used = dataUsed * blockSizeInSectors * 512 |
|
| 1062 |
+ status.Data.Total = dataTotal * blockSizeInSectors * 512 |
|
| 1063 |
+ |
|
| 1064 |
+ // metadata blocks are always 4k |
|
| 1065 |
+ status.Metadata.Used = metadataUsed * 4096 |
|
| 1066 |
+ status.Metadata.Total = metadataTotal * 4096 |
|
| 1067 |
+ |
|
| 1068 |
+ status.SectorSize = blockSizeInSectors * 512 |
|
| 1069 |
+ } |
|
| 1070 |
+ |
|
| 1071 |
+ return status |
|
| 1072 |
+} |
|
| 1073 |
+ |
|
| 1074 |
+func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) {
|
|
| 1075 |
+ SetDevDir("/dev")
|
|
| 1076 |
+ |
|
| 1077 |
+ devices := &DeviceSet{
|
|
| 1078 |
+ root: root, |
|
| 1079 |
+ MetaData: MetaData{Devices: make(map[string]*DevInfo)},
|
|
| 1080 |
+ } |
|
| 1081 |
+ |
|
| 1082 |
+ if err := devices.initDevmapper(doInit); err != nil {
|
|
| 1083 |
+ return nil, err |
|
| 1084 |
+ } |
|
| 1085 |
+ |
|
| 1086 |
+ return devices, nil |
|
| 1087 |
+} |
| 0 | 1088 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,595 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "errors" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "github.com/dotcloud/docker/utils" |
|
| 8 |
+ "runtime" |
|
| 9 |
+ "syscall" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+type DevmapperLogger interface {
|
|
| 13 |
+ log(level int, file string, line int, dmError int, message string) |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+const ( |
|
| 17 |
+ DeviceCreate TaskType = iota |
|
| 18 |
+ DeviceReload |
|
| 19 |
+ DeviceRemove |
|
| 20 |
+ DeviceRemoveAll |
|
| 21 |
+ DeviceSuspend |
|
| 22 |
+ DeviceResume |
|
| 23 |
+ DeviceInfo |
|
| 24 |
+ DeviceDeps |
|
| 25 |
+ DeviceRename |
|
| 26 |
+ DeviceVersion |
|
| 27 |
+ DeviceStatus |
|
| 28 |
+ DeviceTable |
|
| 29 |
+ DeviceWaitevent |
|
| 30 |
+ DeviceList |
|
| 31 |
+ DeviceClear |
|
| 32 |
+ DeviceMknodes |
|
| 33 |
+ DeviceListVersions |
|
| 34 |
+ DeviceTargetMsg |
|
| 35 |
+ DeviceSetGeometry |
|
| 36 |
+) |
|
| 37 |
+ |
|
| 38 |
+const ( |
|
| 39 |
+ AddNodeOnResume AddNodeType = iota |
|
| 40 |
+ AddNodeOnCreate |
|
| 41 |
+) |
|
| 42 |
+ |
|
| 43 |
+var ( |
|
| 44 |
+ ErrTaskRun = errors.New("dm_task_run failed")
|
|
| 45 |
+ ErrTaskSetName = errors.New("dm_task_set_name failed")
|
|
| 46 |
+ ErrTaskSetMessage = errors.New("dm_task_set_message failed")
|
|
| 47 |
+ ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed")
|
|
| 48 |
+ ErrTaskSetRo = errors.New("dm_task_set_ro failed")
|
|
| 49 |
+ ErrTaskAddTarget = errors.New("dm_task_add_target failed")
|
|
| 50 |
+ ErrTaskSetSector = errors.New("dm_task_set_sector failed")
|
|
| 51 |
+ ErrTaskGetInfo = errors.New("dm_task_get_info 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 |
+ ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
|
|
| 63 |
+ ErrLoopbackSetCapacity = errors.New("Unable set loopback capacity")
|
|
| 64 |
+) |
|
| 65 |
+ |
|
| 66 |
+type ( |
|
| 67 |
+ Task struct {
|
|
| 68 |
+ unmanaged *CDmTask |
|
| 69 |
+ } |
|
| 70 |
+ Info struct {
|
|
| 71 |
+ Exists int |
|
| 72 |
+ Suspended int |
|
| 73 |
+ LiveTable int |
|
| 74 |
+ InactiveTable int |
|
| 75 |
+ OpenCount int32 |
|
| 76 |
+ EventNr uint32 |
|
| 77 |
+ Major uint32 |
|
| 78 |
+ Minor uint32 |
|
| 79 |
+ ReadOnly int |
|
| 80 |
+ TargetCount int32 |
|
| 81 |
+ } |
|
| 82 |
+ TaskType int |
|
| 83 |
+ AddNodeType int |
|
| 84 |
+) |
|
| 85 |
+ |
|
| 86 |
+func (t *Task) destroy() {
|
|
| 87 |
+ if t != nil {
|
|
| 88 |
+ DmTaskDestroy(t.unmanaged) |
|
| 89 |
+ runtime.SetFinalizer(t, nil) |
|
| 90 |
+ } |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+func TaskCreate(tasktype TaskType) *Task {
|
|
| 94 |
+ Ctask := DmTaskCreate(int(tasktype)) |
|
| 95 |
+ if Ctask == nil {
|
|
| 96 |
+ return nil |
|
| 97 |
+ } |
|
| 98 |
+ task := &Task{unmanaged: Ctask}
|
|
| 99 |
+ runtime.SetFinalizer(task, (*Task).destroy) |
|
| 100 |
+ return task |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func (t *Task) Run() error {
|
|
| 104 |
+ if res := DmTaskRun(t.unmanaged); res != 1 {
|
|
| 105 |
+ return ErrTaskRun |
|
| 106 |
+ } |
|
| 107 |
+ return nil |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+func (t *Task) SetName(name string) error {
|
|
| 111 |
+ if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
|
| 112 |
+ return ErrTaskSetName |
|
| 113 |
+ } |
|
| 114 |
+ return nil |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+func (t *Task) SetMessage(message string) error {
|
|
| 118 |
+ if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
|
| 119 |
+ return ErrTaskSetMessage |
|
| 120 |
+ } |
|
| 121 |
+ return nil |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+func (t *Task) SetSector(sector uint64) error {
|
|
| 125 |
+ if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
|
| 126 |
+ return ErrTaskSetSector |
|
| 127 |
+ } |
|
| 128 |
+ return nil |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func (t *Task) SetCookie(cookie *uint, flags uint16) error {
|
|
| 132 |
+ if cookie == nil {
|
|
| 133 |
+ return ErrNilCookie |
|
| 134 |
+ } |
|
| 135 |
+ if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
|
|
| 136 |
+ return ErrTaskSetCookie |
|
| 137 |
+ } |
|
| 138 |
+ return nil |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+func (t *Task) SetAddNode(addNode AddNodeType) error {
|
|
| 142 |
+ if addNode != AddNodeOnResume && addNode != AddNodeOnCreate {
|
|
| 143 |
+ return ErrInvalidAddNode |
|
| 144 |
+ } |
|
| 145 |
+ if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
|
| 146 |
+ return ErrTaskSetAddNode |
|
| 147 |
+ } |
|
| 148 |
+ return nil |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+func (t *Task) SetRo() error {
|
|
| 152 |
+ if res := DmTaskSetRo(t.unmanaged); res != 1 {
|
|
| 153 |
+ return ErrTaskSetRo |
|
| 154 |
+ } |
|
| 155 |
+ return nil |
|
| 156 |
+} |
|
| 157 |
+ |
|
| 158 |
+func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
|
|
| 159 |
+ if res := DmTaskAddTarget(t.unmanaged, start, size, |
|
| 160 |
+ ttype, params); res != 1 {
|
|
| 161 |
+ return ErrTaskAddTarget |
|
| 162 |
+ } |
|
| 163 |
+ return nil |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+func (t *Task) GetInfo() (*Info, error) {
|
|
| 167 |
+ info := &Info{}
|
|
| 168 |
+ if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
|
| 169 |
+ return nil, ErrTaskGetInfo |
|
| 170 |
+ } |
|
| 171 |
+ return info, nil |
|
| 172 |
+} |
|
| 173 |
+ |
|
| 174 |
+func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, |
|
| 175 |
+ length uint64, targetType string, params string) {
|
|
| 176 |
+ |
|
| 177 |
+ return DmGetNextTarget(t.unmanaged, next, &start, &length, |
|
| 178 |
+ &targetType, ¶ms), |
|
| 179 |
+ start, length, targetType, params |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 182 |
+func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) {
|
|
| 183 |
+ loopInfo, err := ioctlLoopGetStatus64(file.Fd()) |
|
| 184 |
+ if err != nil {
|
|
| 185 |
+ utils.Errorf("Error get loopback backing file: %s\n", err)
|
|
| 186 |
+ return 0, 0, ErrGetLoopbackBackingFile |
|
| 187 |
+ } |
|
| 188 |
+ return loopInfo.loDevice, loopInfo.loInode, nil |
|
| 189 |
+} |
|
| 190 |
+ |
|
| 191 |
+func LoopbackSetCapacity(file *osFile) error {
|
|
| 192 |
+ if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
|
|
| 193 |
+ utils.Errorf("Error loopbackSetCapacity: %s", err)
|
|
| 194 |
+ return ErrLoopbackSetCapacity |
|
| 195 |
+ } |
|
| 196 |
+ return nil |
|
| 197 |
+} |
|
| 198 |
+ |
|
| 199 |
+func FindLoopDeviceFor(file *osFile) *osFile {
|
|
| 200 |
+ stat, err := file.Stat() |
|
| 201 |
+ if err != nil {
|
|
| 202 |
+ return nil |
|
| 203 |
+ } |
|
| 204 |
+ targetInode := stat.Sys().(*sysStatT).Ino |
|
| 205 |
+ targetDevice := stat.Sys().(*sysStatT).Dev |
|
| 206 |
+ |
|
| 207 |
+ for i := 0; true; i++ {
|
|
| 208 |
+ path := fmt.Sprintf("/dev/loop%d", i)
|
|
| 209 |
+ |
|
| 210 |
+ file, err := osOpenFile(path, osORdWr, 0) |
|
| 211 |
+ if err != nil {
|
|
| 212 |
+ if osIsNotExist(err) {
|
|
| 213 |
+ return nil |
|
| 214 |
+ } |
|
| 215 |
+ |
|
| 216 |
+ // Ignore all errors until the first not-exist |
|
| 217 |
+ // we want to continue looking for the file |
|
| 218 |
+ continue |
|
| 219 |
+ } |
|
| 220 |
+ |
|
| 221 |
+ dev, inode, err := getLoopbackBackingFile(file) |
|
| 222 |
+ if err == nil && dev == targetDevice && inode == targetInode {
|
|
| 223 |
+ return file |
|
| 224 |
+ } |
|
| 225 |
+ file.Close() |
|
| 226 |
+ } |
|
| 227 |
+ |
|
| 228 |
+ return nil |
|
| 229 |
+} |
|
| 230 |
+ |
|
| 231 |
+func UdevWait(cookie uint) error {
|
|
| 232 |
+ if res := DmUdevWait(cookie); res != 1 {
|
|
| 233 |
+ utils.Debugf("Failed to wait on udev cookie %d", cookie)
|
|
| 234 |
+ return ErrUdevWait |
|
| 235 |
+ } |
|
| 236 |
+ return nil |
|
| 237 |
+} |
|
| 238 |
+ |
|
| 239 |
+func LogInitVerbose(level int) {
|
|
| 240 |
+ DmLogInitVerbose(level) |
|
| 241 |
+} |
|
| 242 |
+ |
|
| 243 |
+var dmLogger DevmapperLogger = nil |
|
| 244 |
+ |
|
| 245 |
+func logInit(logger DevmapperLogger) {
|
|
| 246 |
+ dmLogger = logger |
|
| 247 |
+ LogWithErrnoInit() |
|
| 248 |
+} |
|
| 249 |
+ |
|
| 250 |
+func SetDevDir(dir string) error {
|
|
| 251 |
+ if res := DmSetDevDir(dir); res != 1 {
|
|
| 252 |
+ utils.Debugf("Error dm_set_dev_dir")
|
|
| 253 |
+ return ErrSetDevDir |
|
| 254 |
+ } |
|
| 255 |
+ return nil |
|
| 256 |
+} |
|
| 257 |
+ |
|
| 258 |
+func GetLibraryVersion() (string, error) {
|
|
| 259 |
+ var version string |
|
| 260 |
+ if res := DmGetLibraryVersion(&version); res != 1 {
|
|
| 261 |
+ return "", ErrGetLibraryVersion |
|
| 262 |
+ } |
|
| 263 |
+ return version, nil |
|
| 264 |
+} |
|
| 265 |
+ |
|
| 266 |
+// Useful helper for cleanup |
|
| 267 |
+func RemoveDevice(name string) error {
|
|
| 268 |
+ task := TaskCreate(DeviceRemove) |
|
| 269 |
+ if task == nil {
|
|
| 270 |
+ return ErrCreateRemoveTask |
|
| 271 |
+ } |
|
| 272 |
+ if err := task.SetName(name); err != nil {
|
|
| 273 |
+ utils.Debugf("Can't set task name %s", name)
|
|
| 274 |
+ return err |
|
| 275 |
+ } |
|
| 276 |
+ if err := task.Run(); err != nil {
|
|
| 277 |
+ return ErrRunRemoveDevice |
|
| 278 |
+ } |
|
| 279 |
+ return nil |
|
| 280 |
+} |
|
| 281 |
+ |
|
| 282 |
+func GetBlockDeviceSize(file *osFile) (uint64, error) {
|
|
| 283 |
+ size, err := ioctlBlkGetSize64(file.Fd()) |
|
| 284 |
+ if err != nil {
|
|
| 285 |
+ utils.Errorf("Error getblockdevicesize: %s", err)
|
|
| 286 |
+ return 0, ErrGetBlockSize |
|
| 287 |
+ } |
|
| 288 |
+ return uint64(size), nil |
|
| 289 |
+} |
|
| 290 |
+ |
|
| 291 |
+func BlockDeviceDiscard(path string) error {
|
|
| 292 |
+ file, err := osOpenFile(path, osORdWr, 0) |
|
| 293 |
+ if err != nil {
|
|
| 294 |
+ return err |
|
| 295 |
+ } |
|
| 296 |
+ defer file.Close() |
|
| 297 |
+ |
|
| 298 |
+ size, err := GetBlockDeviceSize(file) |
|
| 299 |
+ if err != nil {
|
|
| 300 |
+ return err |
|
| 301 |
+ } |
|
| 302 |
+ |
|
| 303 |
+ if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
|
|
| 304 |
+ return err |
|
| 305 |
+ } |
|
| 306 |
+ |
|
| 307 |
+ // Without this sometimes the remove of the device that happens after |
|
| 308 |
+ // discard fails with EBUSY. |
|
| 309 |
+ syscall.Sync() |
|
| 310 |
+ |
|
| 311 |
+ return nil |
|
| 312 |
+} |
|
| 313 |
+ |
|
| 314 |
+// This is the programmatic example of "dmsetup create" |
|
| 315 |
+func createPool(poolName string, dataFile, metadataFile *osFile) error {
|
|
| 316 |
+ task, err := createTask(DeviceCreate, poolName) |
|
| 317 |
+ if task == nil {
|
|
| 318 |
+ return err |
|
| 319 |
+ } |
|
| 320 |
+ |
|
| 321 |
+ size, err := GetBlockDeviceSize(dataFile) |
|
| 322 |
+ if err != nil {
|
|
| 323 |
+ return fmt.Errorf("Can't get data size")
|
|
| 324 |
+ } |
|
| 325 |
+ |
|
| 326 |
+ params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768 1 skip_block_zeroing" |
|
| 327 |
+ if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 328 |
+ return fmt.Errorf("Can't add target")
|
|
| 329 |
+ } |
|
| 330 |
+ |
|
| 331 |
+ var cookie uint = 0 |
|
| 332 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 333 |
+ return fmt.Errorf("Can't set cookie")
|
|
| 334 |
+ } |
|
| 335 |
+ |
|
| 336 |
+ if err := task.Run(); err != nil {
|
|
| 337 |
+ return fmt.Errorf("Error running DeviceCreate (createPool)")
|
|
| 338 |
+ } |
|
| 339 |
+ |
|
| 340 |
+ UdevWait(cookie) |
|
| 341 |
+ |
|
| 342 |
+ return nil |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+func reloadPool(poolName string, dataFile, metadataFile *osFile) error {
|
|
| 346 |
+ task, err := createTask(DeviceReload, poolName) |
|
| 347 |
+ if task == nil {
|
|
| 348 |
+ return err |
|
| 349 |
+ } |
|
| 350 |
+ |
|
| 351 |
+ size, err := GetBlockDeviceSize(dataFile) |
|
| 352 |
+ if err != nil {
|
|
| 353 |
+ return fmt.Errorf("Can't get data size")
|
|
| 354 |
+ } |
|
| 355 |
+ |
|
| 356 |
+ params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768" |
|
| 357 |
+ if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
|
|
| 358 |
+ return fmt.Errorf("Can't add target")
|
|
| 359 |
+ } |
|
| 360 |
+ |
|
| 361 |
+ if err := task.Run(); err != nil {
|
|
| 362 |
+ return fmt.Errorf("Error running DeviceCreate")
|
|
| 363 |
+ } |
|
| 364 |
+ |
|
| 365 |
+ return nil |
|
| 366 |
+} |
|
| 367 |
+ |
|
| 368 |
+func createTask(t TaskType, name string) (*Task, error) {
|
|
| 369 |
+ task := TaskCreate(t) |
|
| 370 |
+ if task == nil {
|
|
| 371 |
+ return nil, fmt.Errorf("Can't create task of type %d", int(t))
|
|
| 372 |
+ } |
|
| 373 |
+ if err := task.SetName(name); err != nil {
|
|
| 374 |
+ return nil, fmt.Errorf("Can't set task name %s", name)
|
|
| 375 |
+ } |
|
| 376 |
+ return task, nil |
|
| 377 |
+} |
|
| 378 |
+ |
|
| 379 |
+func getInfo(name string) (*Info, error) {
|
|
| 380 |
+ task, err := createTask(DeviceInfo, name) |
|
| 381 |
+ if task == nil {
|
|
| 382 |
+ return nil, err |
|
| 383 |
+ } |
|
| 384 |
+ if err := task.Run(); err != nil {
|
|
| 385 |
+ return nil, err |
|
| 386 |
+ } |
|
| 387 |
+ return task.GetInfo() |
|
| 388 |
+} |
|
| 389 |
+ |
|
| 390 |
+func getStatus(name string) (uint64, uint64, string, string, error) {
|
|
| 391 |
+ task, err := createTask(DeviceStatus, name) |
|
| 392 |
+ if task == nil {
|
|
| 393 |
+ utils.Debugf("getStatus: Error createTask: %s", err)
|
|
| 394 |
+ return 0, 0, "", "", err |
|
| 395 |
+ } |
|
| 396 |
+ if err := task.Run(); err != nil {
|
|
| 397 |
+ utils.Debugf("getStatus: Error Run: %s", err)
|
|
| 398 |
+ return 0, 0, "", "", err |
|
| 399 |
+ } |
|
| 400 |
+ |
|
| 401 |
+ devinfo, err := task.GetInfo() |
|
| 402 |
+ if err != nil {
|
|
| 403 |
+ utils.Debugf("getStatus: Error GetInfo: %s", err)
|
|
| 404 |
+ return 0, 0, "", "", err |
|
| 405 |
+ } |
|
| 406 |
+ if devinfo.Exists == 0 {
|
|
| 407 |
+ utils.Debugf("getStatus: Non existing device %s", name)
|
|
| 408 |
+ return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
|
|
| 409 |
+ } |
|
| 410 |
+ |
|
| 411 |
+ _, start, length, targetType, params := task.GetNextTarget(0) |
|
| 412 |
+ return start, length, targetType, params, nil |
|
| 413 |
+} |
|
| 414 |
+ |
|
| 415 |
+func setTransactionId(poolName string, oldId uint64, newId uint64) error {
|
|
| 416 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 417 |
+ if task == nil {
|
|
| 418 |
+ return err |
|
| 419 |
+ } |
|
| 420 |
+ |
|
| 421 |
+ if err := task.SetSector(0); err != nil {
|
|
| 422 |
+ return fmt.Errorf("Can't set sector")
|
|
| 423 |
+ } |
|
| 424 |
+ |
|
| 425 |
+ if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
|
|
| 426 |
+ return fmt.Errorf("Can't set message")
|
|
| 427 |
+ } |
|
| 428 |
+ |
|
| 429 |
+ if err := task.Run(); err != nil {
|
|
| 430 |
+ return fmt.Errorf("Error running setTransactionId")
|
|
| 431 |
+ } |
|
| 432 |
+ return nil |
|
| 433 |
+} |
|
| 434 |
+ |
|
| 435 |
+func suspendDevice(name string) error {
|
|
| 436 |
+ task, err := createTask(DeviceSuspend, name) |
|
| 437 |
+ if task == nil {
|
|
| 438 |
+ return err |
|
| 439 |
+ } |
|
| 440 |
+ if err := task.Run(); err != nil {
|
|
| 441 |
+ return fmt.Errorf("Error running DeviceSuspend: %s", err)
|
|
| 442 |
+ } |
|
| 443 |
+ return nil |
|
| 444 |
+} |
|
| 445 |
+ |
|
| 446 |
+func resumeDevice(name string) error {
|
|
| 447 |
+ task, err := createTask(DeviceResume, name) |
|
| 448 |
+ if task == nil {
|
|
| 449 |
+ return err |
|
| 450 |
+ } |
|
| 451 |
+ |
|
| 452 |
+ var cookie uint = 0 |
|
| 453 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 454 |
+ return fmt.Errorf("Can't set cookie")
|
|
| 455 |
+ } |
|
| 456 |
+ |
|
| 457 |
+ if err := task.Run(); err != nil {
|
|
| 458 |
+ return fmt.Errorf("Error running DeviceResume")
|
|
| 459 |
+ } |
|
| 460 |
+ |
|
| 461 |
+ UdevWait(cookie) |
|
| 462 |
+ |
|
| 463 |
+ return nil |
|
| 464 |
+} |
|
| 465 |
+ |
|
| 466 |
+func createDevice(poolName string, deviceId int) error {
|
|
| 467 |
+ utils.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
|
|
| 468 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 469 |
+ if task == nil {
|
|
| 470 |
+ return err |
|
| 471 |
+ } |
|
| 472 |
+ |
|
| 473 |
+ if err := task.SetSector(0); err != nil {
|
|
| 474 |
+ return fmt.Errorf("Can't set sector")
|
|
| 475 |
+ } |
|
| 476 |
+ |
|
| 477 |
+ if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
|
|
| 478 |
+ return fmt.Errorf("Can't set message")
|
|
| 479 |
+ } |
|
| 480 |
+ |
|
| 481 |
+ if err := task.Run(); err != nil {
|
|
| 482 |
+ return fmt.Errorf("Error running createDevice")
|
|
| 483 |
+ } |
|
| 484 |
+ return nil |
|
| 485 |
+} |
|
| 486 |
+ |
|
| 487 |
+func deleteDevice(poolName string, deviceId int) error {
|
|
| 488 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 489 |
+ if task == nil {
|
|
| 490 |
+ return err |
|
| 491 |
+ } |
|
| 492 |
+ |
|
| 493 |
+ if err := task.SetSector(0); err != nil {
|
|
| 494 |
+ return fmt.Errorf("Can't set sector")
|
|
| 495 |
+ } |
|
| 496 |
+ |
|
| 497 |
+ if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
|
|
| 498 |
+ return fmt.Errorf("Can't set message")
|
|
| 499 |
+ } |
|
| 500 |
+ |
|
| 501 |
+ if err := task.Run(); err != nil {
|
|
| 502 |
+ return fmt.Errorf("Error running deleteDevice")
|
|
| 503 |
+ } |
|
| 504 |
+ return nil |
|
| 505 |
+} |
|
| 506 |
+ |
|
| 507 |
+func removeDevice(name string) error {
|
|
| 508 |
+ utils.Debugf("[devmapper] removeDevice START")
|
|
| 509 |
+ defer utils.Debugf("[devmapper] removeDevice END")
|
|
| 510 |
+ task, err := createTask(DeviceRemove, name) |
|
| 511 |
+ if task == nil {
|
|
| 512 |
+ return err |
|
| 513 |
+ } |
|
| 514 |
+ if err = task.Run(); err != nil {
|
|
| 515 |
+ return fmt.Errorf("Error running removeDevice")
|
|
| 516 |
+ } |
|
| 517 |
+ return nil |
|
| 518 |
+} |
|
| 519 |
+ |
|
| 520 |
+func activateDevice(poolName string, name string, deviceId int, size uint64) error {
|
|
| 521 |
+ task, err := createTask(DeviceCreate, name) |
|
| 522 |
+ if task == nil {
|
|
| 523 |
+ return err |
|
| 524 |
+ } |
|
| 525 |
+ |
|
| 526 |
+ params := fmt.Sprintf("%s %d", poolName, deviceId)
|
|
| 527 |
+ if err := task.AddTarget(0, size/512, "thin", params); err != nil {
|
|
| 528 |
+ return fmt.Errorf("Can't add target")
|
|
| 529 |
+ } |
|
| 530 |
+ if err := task.SetAddNode(AddNodeOnCreate); err != nil {
|
|
| 531 |
+ return fmt.Errorf("Can't add node")
|
|
| 532 |
+ } |
|
| 533 |
+ |
|
| 534 |
+ var cookie uint = 0 |
|
| 535 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 536 |
+ return fmt.Errorf("Can't set cookie")
|
|
| 537 |
+ } |
|
| 538 |
+ |
|
| 539 |
+ if err := task.Run(); err != nil {
|
|
| 540 |
+ return fmt.Errorf("Error running DeviceCreate (activateDevice)")
|
|
| 541 |
+ } |
|
| 542 |
+ |
|
| 543 |
+ UdevWait(cookie) |
|
| 544 |
+ |
|
| 545 |
+ return nil |
|
| 546 |
+} |
|
| 547 |
+ |
|
| 548 |
+func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
|
|
| 549 |
+ devinfo, _ := getInfo(baseName) |
|
| 550 |
+ doSuspend := devinfo != nil && devinfo.Exists != 0 |
|
| 551 |
+ |
|
| 552 |
+ if doSuspend {
|
|
| 553 |
+ if err := suspendDevice(baseName); err != nil {
|
|
| 554 |
+ return err |
|
| 555 |
+ } |
|
| 556 |
+ } |
|
| 557 |
+ |
|
| 558 |
+ task, err := createTask(DeviceTargetMsg, poolName) |
|
| 559 |
+ if task == nil {
|
|
| 560 |
+ if doSuspend {
|
|
| 561 |
+ resumeDevice(baseName) |
|
| 562 |
+ } |
|
| 563 |
+ return err |
|
| 564 |
+ } |
|
| 565 |
+ |
|
| 566 |
+ if err := task.SetSector(0); err != nil {
|
|
| 567 |
+ if doSuspend {
|
|
| 568 |
+ resumeDevice(baseName) |
|
| 569 |
+ } |
|
| 570 |
+ return fmt.Errorf("Can't set sector")
|
|
| 571 |
+ } |
|
| 572 |
+ |
|
| 573 |
+ if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
|
|
| 574 |
+ if doSuspend {
|
|
| 575 |
+ resumeDevice(baseName) |
|
| 576 |
+ } |
|
| 577 |
+ return fmt.Errorf("Can't set message")
|
|
| 578 |
+ } |
|
| 579 |
+ |
|
| 580 |
+ if err := task.Run(); err != nil {
|
|
| 581 |
+ if doSuspend {
|
|
| 582 |
+ resumeDevice(baseName) |
|
| 583 |
+ } |
|
| 584 |
+ return fmt.Errorf("Error running DeviceCreate (createSnapDevice)")
|
|
| 585 |
+ } |
|
| 586 |
+ |
|
| 587 |
+ if doSuspend {
|
|
| 588 |
+ if err := resumeDevice(baseName); err != nil {
|
|
| 589 |
+ return err |
|
| 590 |
+ } |
|
| 591 |
+ } |
|
| 592 |
+ |
|
| 593 |
+ return nil |
|
| 594 |
+} |
| 0 | 595 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,106 @@ |
| 0 |
+package devmapper |
|
| 1 |
+ |
|
| 2 |
+// Definition of struct dm_task and sub structures (from lvm2) |
|
| 3 |
+// |
|
| 4 |
+// struct dm_ioctl {
|
|
| 5 |
+// /* |
|
| 6 |
+// * The version number is made up of three parts: |
|
| 7 |
+// * major - no backward or forward compatibility, |
|
| 8 |
+// * minor - only backwards compatible, |
|
| 9 |
+// * patch - both backwards and forwards compatible. |
|
| 10 |
+// * |
|
| 11 |
+// * All clients of the ioctl interface should fill in the |
|
| 12 |
+// * version number of the interface that they were |
|
| 13 |
+// * compiled with. |
|
| 14 |
+// * |
|
| 15 |
+// * All recognised ioctl commands (ie. those that don't |
|
| 16 |
+// * return -ENOTTY) fill out this field, even if the |
|
| 17 |
+// * command failed. |
|
| 18 |
+// */ |
|
| 19 |
+// uint32_t version[3]; /* in/out */ |
|
| 20 |
+// uint32_t data_size; /* total size of data passed in |
|
| 21 |
+// * including this struct */ |
|
| 22 |
+ |
|
| 23 |
+// uint32_t data_start; /* offset to start of data |
|
| 24 |
+// * relative to start of this struct */ |
|
| 25 |
+ |
|
| 26 |
+// uint32_t target_count; /* in/out */ |
|
| 27 |
+// int32_t open_count; /* out */ |
|
| 28 |
+// uint32_t flags; /* in/out */ |
|
| 29 |
+ |
|
| 30 |
+// /* |
|
| 31 |
+// * event_nr holds either the event number (input and output) or the |
|
| 32 |
+// * udev cookie value (input only). |
|
| 33 |
+// * The DM_DEV_WAIT ioctl takes an event number as input. |
|
| 34 |
+// * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls |
|
| 35 |
+// * use the field as a cookie to return in the DM_COOKIE |
|
| 36 |
+// * variable with the uevents they issue. |
|
| 37 |
+// * For output, the ioctls return the event number, not the cookie. |
|
| 38 |
+// */ |
|
| 39 |
+// uint32_t event_nr; /* in/out */ |
|
| 40 |
+// uint32_t padding; |
|
| 41 |
+ |
|
| 42 |
+// uint64_t dev; /* in/out */ |
|
| 43 |
+ |
|
| 44 |
+// char name[DM_NAME_LEN]; /* device name */ |
|
| 45 |
+// char uuid[DM_UUID_LEN]; /* unique identifier for |
|
| 46 |
+// * the block device */ |
|
| 47 |
+// char data[7]; /* padding or data */ |
|
| 48 |
+// }; |
|
| 49 |
+ |
|
| 50 |
+// struct target {
|
|
| 51 |
+// uint64_t start; |
|
| 52 |
+// uint64_t length; |
|
| 53 |
+// char *type; |
|
| 54 |
+// char *params; |
|
| 55 |
+ |
|
| 56 |
+// struct target *next; |
|
| 57 |
+// }; |
|
| 58 |
+ |
|
| 59 |
+// typedef enum {
|
|
| 60 |
+// DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */ |
|
| 61 |
+// DM_ADD_NODE_ON_CREATE /* add /dev/mapper node with dmsetup create */ |
|
| 62 |
+// } dm_add_node_t; |
|
| 63 |
+ |
|
| 64 |
+// struct dm_task {
|
|
| 65 |
+// int type; |
|
| 66 |
+// char *dev_name; |
|
| 67 |
+// char *mangled_dev_name; |
|
| 68 |
+ |
|
| 69 |
+// struct target *head, *tail; |
|
| 70 |
+ |
|
| 71 |
+// int read_only; |
|
| 72 |
+// uint32_t event_nr; |
|
| 73 |
+// int major; |
|
| 74 |
+// int minor; |
|
| 75 |
+// int allow_default_major_fallback; |
|
| 76 |
+// uid_t uid; |
|
| 77 |
+// gid_t gid; |
|
| 78 |
+// mode_t mode; |
|
| 79 |
+// uint32_t read_ahead; |
|
| 80 |
+// uint32_t read_ahead_flags; |
|
| 81 |
+// union {
|
|
| 82 |
+// struct dm_ioctl *v4; |
|
| 83 |
+// } dmi; |
|
| 84 |
+// char *newname; |
|
| 85 |
+// char *message; |
|
| 86 |
+// char *geometry; |
|
| 87 |
+// uint64_t sector; |
|
| 88 |
+// int no_flush; |
|
| 89 |
+// int no_open_count; |
|
| 90 |
+// int skip_lockfs; |
|
| 91 |
+// int query_inactive_table; |
|
| 92 |
+// int suppress_identical_reload; |
|
| 93 |
+// dm_add_node_t add_node; |
|
| 94 |
+// uint64_t existing_table_size; |
|
| 95 |
+// int cookie_set; |
|
| 96 |
+// int new_uuid; |
|
| 97 |
+// int secure_data; |
|
| 98 |
+// int retry_remove; |
|
| 99 |
+// int enable_checks; |
|
| 100 |
+// int expected_errno; |
|
| 101 |
+ |
|
| 102 |
+// char *uuid; |
|
| 103 |
+// char *mangled_uuid; |
|
| 104 |
+// }; |
|
| 105 |
+// |
| 0 | 106 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import "C" |
|
| 5 |
+ |
|
| 6 |
+// Due to the way cgo works this has to be in a separate file, as devmapper.go has |
|
| 7 |
+// definitions in the cgo block, which is incompatible with using "//export" |
|
| 8 |
+ |
|
| 9 |
+//export DevmapperLogCallback |
|
| 10 |
+func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_class C.int, message *C.char) {
|
|
| 11 |
+ if dmLogger != nil {
|
|
| 12 |
+ dmLogger.log(int(level), C.GoString(file), int(line), int(dm_errno_or_class), C.GoString(message)) |
|
| 13 |
+ } |
|
| 14 |
+} |
| 0 | 15 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,287 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "testing" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func TestTaskCreate(t *testing.T) {
|
|
| 9 |
+ t.Skip("FIXME: not a unit test")
|
|
| 10 |
+ // Test success |
|
| 11 |
+ taskCreate(t, DeviceInfo) |
|
| 12 |
+ |
|
| 13 |
+ // Test Failure |
|
| 14 |
+ DmTaskCreate = dmTaskCreateFail |
|
| 15 |
+ defer func() { DmTaskCreate = dmTaskCreateFct }()
|
|
| 16 |
+ if task := TaskCreate(-1); task != nil {
|
|
| 17 |
+ t.Fatalf("An error should have occured while creating an invalid task.")
|
|
| 18 |
+ } |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func TestTaskRun(t *testing.T) {
|
|
| 22 |
+ t.Skip("FIXME: not a unit test")
|
|
| 23 |
+ task := taskCreate(t, DeviceInfo) |
|
| 24 |
+ |
|
| 25 |
+ // Test success |
|
| 26 |
+ // Perform the RUN |
|
| 27 |
+ if err := task.Run(); err != nil {
|
|
| 28 |
+ t.Fatal(err) |
|
| 29 |
+ } |
|
| 30 |
+ // Make sure we don't have error with GetInfo |
|
| 31 |
+ if _, err := task.GetInfo(); err != nil {
|
|
| 32 |
+ t.Fatal(err) |
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 35 |
+ // Test failure |
|
| 36 |
+ DmTaskRun = dmTaskRunFail |
|
| 37 |
+ defer func() { DmTaskRun = dmTaskRunFct }()
|
|
| 38 |
+ |
|
| 39 |
+ task = taskCreate(t, DeviceInfo) |
|
| 40 |
+ // Perform the RUN |
|
| 41 |
+ if err := task.Run(); err != ErrTaskRun {
|
|
| 42 |
+ t.Fatalf("An error should have occured while running task.")
|
|
| 43 |
+ } |
|
| 44 |
+ // Make sure GetInfo also fails |
|
| 45 |
+ if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 46 |
+ t.Fatalf("GetInfo should fail if task.Run() failed.")
|
|
| 47 |
+ } |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func TestTaskSetName(t *testing.T) {
|
|
| 51 |
+ t.Skip("FIXME: not a unit test")
|
|
| 52 |
+ task := taskCreate(t, DeviceInfo) |
|
| 53 |
+ |
|
| 54 |
+ // Test success |
|
| 55 |
+ if err := task.SetName("test"); err != nil {
|
|
| 56 |
+ t.Fatal(err) |
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ // Test failure |
|
| 60 |
+ DmTaskSetName = dmTaskSetNameFail |
|
| 61 |
+ defer func() { DmTaskSetName = dmTaskSetNameFct }()
|
|
| 62 |
+ |
|
| 63 |
+ if err := task.SetName("test"); err != ErrTaskSetName {
|
|
| 64 |
+ t.Fatalf("An error should have occured while runnign SetName.")
|
|
| 65 |
+ } |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+func TestTaskSetMessage(t *testing.T) {
|
|
| 69 |
+ t.Skip("FIXME: not a unit test")
|
|
| 70 |
+ task := taskCreate(t, DeviceInfo) |
|
| 71 |
+ |
|
| 72 |
+ // Test success |
|
| 73 |
+ if err := task.SetMessage("test"); err != nil {
|
|
| 74 |
+ t.Fatal(err) |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ // Test failure |
|
| 78 |
+ DmTaskSetMessage = dmTaskSetMessageFail |
|
| 79 |
+ defer func() { DmTaskSetMessage = dmTaskSetMessageFct }()
|
|
| 80 |
+ |
|
| 81 |
+ if err := task.SetMessage("test"); err != ErrTaskSetMessage {
|
|
| 82 |
+ t.Fatalf("An error should have occured while runnign SetMessage.")
|
|
| 83 |
+ } |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+func TestTaskSetSector(t *testing.T) {
|
|
| 87 |
+ t.Skip("FIXME: not a unit test")
|
|
| 88 |
+ task := taskCreate(t, DeviceInfo) |
|
| 89 |
+ |
|
| 90 |
+ // Test success |
|
| 91 |
+ if err := task.SetSector(128); err != nil {
|
|
| 92 |
+ t.Fatal(err) |
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ DmTaskSetSector = dmTaskSetSectorFail |
|
| 96 |
+ defer func() { DmTaskSetSector = dmTaskSetSectorFct }()
|
|
| 97 |
+ |
|
| 98 |
+ // Test failure |
|
| 99 |
+ if err := task.SetSector(0); err != ErrTaskSetSector {
|
|
| 100 |
+ t.Fatalf("An error should have occured while running SetSector.")
|
|
| 101 |
+ } |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+func TestTaskSetCookie(t *testing.T) {
|
|
| 105 |
+ t.Skip("FIXME: not a unit test")
|
|
| 106 |
+ var ( |
|
| 107 |
+ cookie uint = 0 |
|
| 108 |
+ task = taskCreate(t, DeviceInfo) |
|
| 109 |
+ ) |
|
| 110 |
+ |
|
| 111 |
+ // Test success |
|
| 112 |
+ if err := task.SetCookie(&cookie, 0); err != nil {
|
|
| 113 |
+ t.Fatal(err) |
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ // Test failure |
|
| 117 |
+ if err := task.SetCookie(nil, 0); err != ErrNilCookie {
|
|
| 118 |
+ t.Fatalf("An error should have occured while running SetCookie with nil cookie.")
|
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ DmTaskSetCookie = dmTaskSetCookieFail |
|
| 122 |
+ defer func() { DmTaskSetCookie = dmTaskSetCookieFct }()
|
|
| 123 |
+ |
|
| 124 |
+ if err := task.SetCookie(&cookie, 0); err != ErrTaskSetCookie {
|
|
| 125 |
+ t.Fatalf("An error should have occured while running SetCookie.")
|
|
| 126 |
+ } |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+func TestTaskSetAddNode(t *testing.T) {
|
|
| 130 |
+ t.Skip("FIXME: not a unit test")
|
|
| 131 |
+ task := taskCreate(t, DeviceInfo) |
|
| 132 |
+ |
|
| 133 |
+ // Test success |
|
| 134 |
+ if err := task.SetAddNode(0); err != nil {
|
|
| 135 |
+ t.Fatal(err) |
|
| 136 |
+ } |
|
| 137 |
+ |
|
| 138 |
+ // Test failure |
|
| 139 |
+ if err := task.SetAddNode(-1); err != ErrInvalidAddNode {
|
|
| 140 |
+ t.Fatalf("An error should have occured running SetAddNode with wrong node.")
|
|
| 141 |
+ } |
|
| 142 |
+ |
|
| 143 |
+ DmTaskSetAddNode = dmTaskSetAddNodeFail |
|
| 144 |
+ defer func() { DmTaskSetAddNode = dmTaskSetAddNodeFct }()
|
|
| 145 |
+ |
|
| 146 |
+ if err := task.SetAddNode(0); err != ErrTaskSetAddNode {
|
|
| 147 |
+ t.Fatalf("An error should have occured running SetAddNode.")
|
|
| 148 |
+ } |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+func TestTaskSetRo(t *testing.T) {
|
|
| 152 |
+ t.Skip("FIXME: not a unit test")
|
|
| 153 |
+ task := taskCreate(t, DeviceInfo) |
|
| 154 |
+ |
|
| 155 |
+ // Test success |
|
| 156 |
+ if err := task.SetRo(); err != nil {
|
|
| 157 |
+ t.Fatal(err) |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+ // Test failure |
|
| 161 |
+ DmTaskSetRo = dmTaskSetRoFail |
|
| 162 |
+ defer func() { DmTaskSetRo = dmTaskSetRoFct }()
|
|
| 163 |
+ |
|
| 164 |
+ if err := task.SetRo(); err != ErrTaskSetRo {
|
|
| 165 |
+ t.Fatalf("An error should have occured running SetRo.")
|
|
| 166 |
+ } |
|
| 167 |
+} |
|
| 168 |
+ |
|
| 169 |
+func TestTaskAddTarget(t *testing.T) {
|
|
| 170 |
+ t.Skip("FIXME: not a unit test")
|
|
| 171 |
+ task := taskCreate(t, DeviceInfo) |
|
| 172 |
+ |
|
| 173 |
+ // Test success |
|
| 174 |
+ if err := task.AddTarget(0, 128, "thinp", ""); err != nil {
|
|
| 175 |
+ t.Fatal(err) |
|
| 176 |
+ } |
|
| 177 |
+ |
|
| 178 |
+ // Test failure |
|
| 179 |
+ DmTaskAddTarget = dmTaskAddTargetFail |
|
| 180 |
+ defer func() { DmTaskAddTarget = dmTaskAddTargetFct }()
|
|
| 181 |
+ |
|
| 182 |
+ if err := task.AddTarget(0, 128, "thinp", ""); err != ErrTaskAddTarget {
|
|
| 183 |
+ t.Fatalf("An error should have occured running AddTarget.")
|
|
| 184 |
+ } |
|
| 185 |
+} |
|
| 186 |
+ |
|
| 187 |
+// func TestTaskGetInfo(t *testing.T) {
|
|
| 188 |
+// task := taskCreate(t, DeviceInfo) |
|
| 189 |
+ |
|
| 190 |
+// // Test success |
|
| 191 |
+// if _, err := task.GetInfo(); err != nil {
|
|
| 192 |
+// t.Fatal(err) |
|
| 193 |
+// } |
|
| 194 |
+ |
|
| 195 |
+// // Test failure |
|
| 196 |
+// DmTaskGetInfo = dmTaskGetInfoFail |
|
| 197 |
+// defer func() { DmTaskGetInfo = dmTaskGetInfoFct }()
|
|
| 198 |
+ |
|
| 199 |
+// if _, err := task.GetInfo(); err != ErrTaskGetInfo {
|
|
| 200 |
+// t.Fatalf("An error should have occured running GetInfo.")
|
|
| 201 |
+// } |
|
| 202 |
+// } |
|
| 203 |
+ |
|
| 204 |
+// func TestTaskGetNextTarget(t *testing.T) {
|
|
| 205 |
+// task := taskCreate(t, DeviceInfo) |
|
| 206 |
+ |
|
| 207 |
+// if next, _, _, _, _ := task.GetNextTarget(0); next == 0 {
|
|
| 208 |
+// t.Fatalf("The next target should not be 0.")
|
|
| 209 |
+// } |
|
| 210 |
+// } |
|
| 211 |
+ |
|
| 212 |
+/// Utils |
|
| 213 |
+func taskCreate(t *testing.T, taskType TaskType) *Task {
|
|
| 214 |
+ task := TaskCreate(taskType) |
|
| 215 |
+ if task == nil {
|
|
| 216 |
+ t.Fatalf("Error creating task")
|
|
| 217 |
+ } |
|
| 218 |
+ return task |
|
| 219 |
+} |
|
| 220 |
+ |
|
| 221 |
+/// Failure function replacement |
|
| 222 |
+func dmTaskCreateFail(t int) *CDmTask {
|
|
| 223 |
+ return nil |
|
| 224 |
+} |
|
| 225 |
+ |
|
| 226 |
+func dmTaskRunFail(task *CDmTask) int {
|
|
| 227 |
+ return -1 |
|
| 228 |
+} |
|
| 229 |
+ |
|
| 230 |
+func dmTaskSetNameFail(task *CDmTask, name string) int {
|
|
| 231 |
+ return -1 |
|
| 232 |
+} |
|
| 233 |
+ |
|
| 234 |
+func dmTaskSetMessageFail(task *CDmTask, message string) int {
|
|
| 235 |
+ return -1 |
|
| 236 |
+} |
|
| 237 |
+ |
|
| 238 |
+func dmTaskSetSectorFail(task *CDmTask, sector uint64) int {
|
|
| 239 |
+ return -1 |
|
| 240 |
+} |
|
| 241 |
+ |
|
| 242 |
+func dmTaskSetCookieFail(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 243 |
+ return -1 |
|
| 244 |
+} |
|
| 245 |
+ |
|
| 246 |
+func dmTaskSetAddNodeFail(task *CDmTask, addNode AddNodeType) int {
|
|
| 247 |
+ return -1 |
|
| 248 |
+} |
|
| 249 |
+ |
|
| 250 |
+func dmTaskSetRoFail(task *CDmTask) int {
|
|
| 251 |
+ return -1 |
|
| 252 |
+} |
|
| 253 |
+ |
|
| 254 |
+func dmTaskAddTargetFail(task *CDmTask, |
|
| 255 |
+ start, size uint64, ttype, params string) int {
|
|
| 256 |
+ return -1 |
|
| 257 |
+} |
|
| 258 |
+ |
|
| 259 |
+func dmTaskGetInfoFail(task *CDmTask, info *Info) int {
|
|
| 260 |
+ return -1 |
|
| 261 |
+} |
|
| 262 |
+ |
|
| 263 |
+func dmGetNextTargetFail(task *CDmTask, next uintptr, start, length *uint64, |
|
| 264 |
+ target, params *string) uintptr {
|
|
| 265 |
+ return 0 |
|
| 266 |
+} |
|
| 267 |
+ |
|
| 268 |
+func dmAttachLoopDeviceFail(filename string, fd *int) string {
|
|
| 269 |
+ return "" |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+func sysGetBlockSizeFail(fd uintptr, size *uint64) sysErrno {
|
|
| 273 |
+ return 1 |
|
| 274 |
+} |
|
| 275 |
+ |
|
| 276 |
+func dmUdevWaitFail(cookie uint) int {
|
|
| 277 |
+ return -1 |
|
| 278 |
+} |
|
| 279 |
+ |
|
| 280 |
+func dmSetDevDirFail(dir string) int {
|
|
| 281 |
+ return -1 |
|
| 282 |
+} |
|
| 283 |
+ |
|
| 284 |
+func dmGetLibraryVersionFail(version *string) int {
|
|
| 285 |
+ return -1 |
|
| 286 |
+} |
| 0 | 287 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,229 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+/* |
|
| 5 |
+#cgo LDFLAGS: -L. -ldevmapper |
|
| 6 |
+#include <libdevmapper.h> |
|
| 7 |
+#include <linux/loop.h> // FIXME: present only for defines, maybe we can remove it? |
|
| 8 |
+#include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it? |
|
| 9 |
+ |
|
| 10 |
+#ifndef LOOP_CTL_GET_FREE |
|
| 11 |
+ #define LOOP_CTL_GET_FREE 0x4C82 |
|
| 12 |
+#endif |
|
| 13 |
+ |
|
| 14 |
+#ifndef LO_FLAGS_PARTSCAN |
|
| 15 |
+ #define LO_FLAGS_PARTSCAN 8 |
|
| 16 |
+#endif |
|
| 17 |
+ |
|
| 18 |
+// FIXME: Can't we find a way to do the logging in pure Go? |
|
| 19 |
+extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); |
|
| 20 |
+ |
|
| 21 |
+static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...) |
|
| 22 |
+{
|
|
| 23 |
+ char buffer[256]; |
|
| 24 |
+ va_list ap; |
|
| 25 |
+ |
|
| 26 |
+ va_start(ap, f); |
|
| 27 |
+ vsnprintf(buffer, 256, f, ap); |
|
| 28 |
+ va_end(ap); |
|
| 29 |
+ |
|
| 30 |
+ DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+static void log_with_errno_init() |
|
| 34 |
+{
|
|
| 35 |
+ dm_log_with_errno_init(log_cb); |
|
| 36 |
+} |
|
| 37 |
+*/ |
|
| 38 |
+import "C" |
|
| 39 |
+ |
|
| 40 |
+import ( |
|
| 41 |
+ "unsafe" |
|
| 42 |
+) |
|
| 43 |
+ |
|
| 44 |
+type ( |
|
| 45 |
+ CDmTask C.struct_dm_task |
|
| 46 |
+ |
|
| 47 |
+ CLoopInfo64 C.struct_loop_info64 |
|
| 48 |
+ LoopInfo64 struct {
|
|
| 49 |
+ loDevice uint64 /* ioctl r/o */ |
|
| 50 |
+ loInode uint64 /* ioctl r/o */ |
|
| 51 |
+ loRdevice uint64 /* ioctl r/o */ |
|
| 52 |
+ loOffset uint64 |
|
| 53 |
+ loSizelimit uint64 /* bytes, 0 == max available */ |
|
| 54 |
+ loNumber uint32 /* ioctl r/o */ |
|
| 55 |
+ loEncrypt_type uint32 |
|
| 56 |
+ loEncrypt_key_size uint32 /* ioctl w/o */ |
|
| 57 |
+ loFlags uint32 /* ioctl r/o */ |
|
| 58 |
+ loFileName [LoNameSize]uint8 |
|
| 59 |
+ loCryptName [LoNameSize]uint8 |
|
| 60 |
+ loEncryptKey [LoKeySize]uint8 /* ioctl w/o */ |
|
| 61 |
+ loInit [2]uint64 |
|
| 62 |
+ } |
|
| 63 |
+) |
|
| 64 |
+ |
|
| 65 |
+// IOCTL consts |
|
| 66 |
+const ( |
|
| 67 |
+ BlkGetSize64 = C.BLKGETSIZE64 |
|
| 68 |
+ BlkDiscard = C.BLKDISCARD |
|
| 69 |
+ |
|
| 70 |
+ LoopSetFd = C.LOOP_SET_FD |
|
| 71 |
+ LoopCtlGetFree = C.LOOP_CTL_GET_FREE |
|
| 72 |
+ LoopGetStatus64 = C.LOOP_GET_STATUS64 |
|
| 73 |
+ LoopSetStatus64 = C.LOOP_SET_STATUS64 |
|
| 74 |
+ LoopClrFd = C.LOOP_CLR_FD |
|
| 75 |
+ LoopSetCapacity = C.LOOP_SET_CAPACITY |
|
| 76 |
+) |
|
| 77 |
+ |
|
| 78 |
+const ( |
|
| 79 |
+ LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR |
|
| 80 |
+ LoFlagsReadOnly = C.LO_FLAGS_READ_ONLY |
|
| 81 |
+ LoFlagsPartScan = C.LO_FLAGS_PARTSCAN |
|
| 82 |
+ LoKeySize = C.LO_KEY_SIZE |
|
| 83 |
+ LoNameSize = C.LO_NAME_SIZE |
|
| 84 |
+) |
|
| 85 |
+ |
|
| 86 |
+var ( |
|
| 87 |
+ DmGetLibraryVersion = dmGetLibraryVersionFct |
|
| 88 |
+ DmGetNextTarget = dmGetNextTargetFct |
|
| 89 |
+ DmLogInitVerbose = dmLogInitVerboseFct |
|
| 90 |
+ DmSetDevDir = dmSetDevDirFct |
|
| 91 |
+ DmTaskAddTarget = dmTaskAddTargetFct |
|
| 92 |
+ DmTaskCreate = dmTaskCreateFct |
|
| 93 |
+ DmTaskDestroy = dmTaskDestroyFct |
|
| 94 |
+ DmTaskGetInfo = dmTaskGetInfoFct |
|
| 95 |
+ DmTaskRun = dmTaskRunFct |
|
| 96 |
+ DmTaskSetAddNode = dmTaskSetAddNodeFct |
|
| 97 |
+ DmTaskSetCookie = dmTaskSetCookieFct |
|
| 98 |
+ DmTaskSetMessage = dmTaskSetMessageFct |
|
| 99 |
+ DmTaskSetName = dmTaskSetNameFct |
|
| 100 |
+ DmTaskSetRo = dmTaskSetRoFct |
|
| 101 |
+ DmTaskSetSector = dmTaskSetSectorFct |
|
| 102 |
+ DmUdevWait = dmUdevWaitFct |
|
| 103 |
+ LogWithErrnoInit = logWithErrnoInitFct |
|
| 104 |
+) |
|
| 105 |
+ |
|
| 106 |
+func free(p *C.char) {
|
|
| 107 |
+ C.free(unsafe.Pointer(p)) |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+func dmTaskDestroyFct(task *CDmTask) {
|
|
| 111 |
+ C.dm_task_destroy((*C.struct_dm_task)(task)) |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func dmTaskCreateFct(taskType int) *CDmTask {
|
|
| 115 |
+ return (*CDmTask)(C.dm_task_create(C.int(taskType))) |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+func dmTaskRunFct(task *CDmTask) int {
|
|
| 119 |
+ ret, _ := C.dm_task_run((*C.struct_dm_task)(task)) |
|
| 120 |
+ return int(ret) |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 123 |
+func dmTaskSetNameFct(task *CDmTask, name string) int {
|
|
| 124 |
+ Cname := C.CString(name) |
|
| 125 |
+ defer free(Cname) |
|
| 126 |
+ |
|
| 127 |
+ return int(C.dm_task_set_name((*C.struct_dm_task)(task), Cname)) |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+func dmTaskSetMessageFct(task *CDmTask, message string) int {
|
|
| 131 |
+ Cmessage := C.CString(message) |
|
| 132 |
+ defer free(Cmessage) |
|
| 133 |
+ |
|
| 134 |
+ return int(C.dm_task_set_message((*C.struct_dm_task)(task), Cmessage)) |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+func dmTaskSetSectorFct(task *CDmTask, sector uint64) int {
|
|
| 138 |
+ return int(C.dm_task_set_sector((*C.struct_dm_task)(task), C.uint64_t(sector))) |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 142 |
+ cCookie := C.uint32_t(*cookie) |
|
| 143 |
+ defer func() {
|
|
| 144 |
+ *cookie = uint(cCookie) |
|
| 145 |
+ }() |
|
| 146 |
+ return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, C.uint16_t(flags))) |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int {
|
|
| 150 |
+ return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode))) |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+func dmTaskSetRoFct(task *CDmTask) int {
|
|
| 154 |
+ return int(C.dm_task_set_ro((*C.struct_dm_task)(task))) |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+func dmTaskAddTargetFct(task *CDmTask, |
|
| 158 |
+ start, size uint64, ttype, params string) int {
|
|
| 159 |
+ |
|
| 160 |
+ Cttype := C.CString(ttype) |
|
| 161 |
+ defer free(Cttype) |
|
| 162 |
+ |
|
| 163 |
+ Cparams := C.CString(params) |
|
| 164 |
+ defer free(Cparams) |
|
| 165 |
+ |
|
| 166 |
+ return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) |
|
| 167 |
+} |
|
| 168 |
+ |
|
| 169 |
+func dmTaskGetInfoFct(task *CDmTask, info *Info) int {
|
|
| 170 |
+ Cinfo := C.struct_dm_info{}
|
|
| 171 |
+ defer func() {
|
|
| 172 |
+ info.Exists = int(Cinfo.exists) |
|
| 173 |
+ info.Suspended = int(Cinfo.suspended) |
|
| 174 |
+ info.LiveTable = int(Cinfo.live_table) |
|
| 175 |
+ info.InactiveTable = int(Cinfo.inactive_table) |
|
| 176 |
+ info.OpenCount = int32(Cinfo.open_count) |
|
| 177 |
+ info.EventNr = uint32(Cinfo.event_nr) |
|
| 178 |
+ info.Major = uint32(Cinfo.major) |
|
| 179 |
+ info.Minor = uint32(Cinfo.minor) |
|
| 180 |
+ info.ReadOnly = int(Cinfo.read_only) |
|
| 181 |
+ info.TargetCount = int32(Cinfo.target_count) |
|
| 182 |
+ }() |
|
| 183 |
+ return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 186 |
+func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
|
|
| 187 |
+ var ( |
|
| 188 |
+ Cstart, Clength C.uint64_t |
|
| 189 |
+ CtargetType, Cparams *C.char |
|
| 190 |
+ ) |
|
| 191 |
+ defer func() {
|
|
| 192 |
+ *start = uint64(Cstart) |
|
| 193 |
+ *length = uint64(Clength) |
|
| 194 |
+ *target = C.GoString(CtargetType) |
|
| 195 |
+ *params = C.GoString(Cparams) |
|
| 196 |
+ }() |
|
| 197 |
+ |
|
| 198 |
+ nextp := C.dm_get_next_target((*C.struct_dm_task)(task), unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) |
|
| 199 |
+ return uintptr(nextp) |
|
| 200 |
+} |
|
| 201 |
+ |
|
| 202 |
+func dmUdevWaitFct(cookie uint) int {
|
|
| 203 |
+ return int(C.dm_udev_wait(C.uint32_t(cookie))) |
|
| 204 |
+} |
|
| 205 |
+ |
|
| 206 |
+func dmLogInitVerboseFct(level int) {
|
|
| 207 |
+ C.dm_log_init_verbose(C.int(level)) |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+func logWithErrnoInitFct() {
|
|
| 211 |
+ C.log_with_errno_init() |
|
| 212 |
+} |
|
| 213 |
+ |
|
| 214 |
+func dmSetDevDirFct(dir string) int {
|
|
| 215 |
+ Cdir := C.CString(dir) |
|
| 216 |
+ defer free(Cdir) |
|
| 217 |
+ |
|
| 218 |
+ return int(C.dm_set_dev_dir(Cdir)) |
|
| 219 |
+} |
|
| 220 |
+ |
|
| 221 |
+func dmGetLibraryVersionFct(version *string) int {
|
|
| 222 |
+ buffer := C.CString(string(make([]byte, 128))) |
|
| 223 |
+ defer free(buffer) |
|
| 224 |
+ defer func() {
|
|
| 225 |
+ *version = C.GoString(buffer) |
|
| 226 |
+ }() |
|
| 227 |
+ return int(C.dm_get_library_version(buffer, 128)) |
|
| 228 |
+} |
| 0 | 229 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,143 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 7 |
+ "github.com/dotcloud/docker/utils" |
|
| 8 |
+ "io/ioutil" |
|
| 9 |
+ "os" |
|
| 10 |
+ "path" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func init() {
|
|
| 14 |
+ graphdriver.Register("devicemapper", Init)
|
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+// Placeholder interfaces, to be replaced |
|
| 18 |
+// at integration. |
|
| 19 |
+ |
|
| 20 |
+// End of placeholder interfaces. |
|
| 21 |
+ |
|
| 22 |
+type Driver struct {
|
|
| 23 |
+ *DeviceSet |
|
| 24 |
+ home string |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+var Init = func(home string) (graphdriver.Driver, error) {
|
|
| 28 |
+ deviceSet, err := NewDeviceSet(home, true) |
|
| 29 |
+ if err != nil {
|
|
| 30 |
+ return nil, err |
|
| 31 |
+ } |
|
| 32 |
+ d := &Driver{
|
|
| 33 |
+ DeviceSet: deviceSet, |
|
| 34 |
+ home: home, |
|
| 35 |
+ } |
|
| 36 |
+ return d, nil |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func (d *Driver) String() string {
|
|
| 40 |
+ return "devicemapper" |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func (d *Driver) Status() [][2]string {
|
|
| 44 |
+ s := d.DeviceSet.Status() |
|
| 45 |
+ |
|
| 46 |
+ status := [][2]string{
|
|
| 47 |
+ {"Pool Name", s.PoolName},
|
|
| 48 |
+ {"Data file", s.DataLoopback},
|
|
| 49 |
+ {"Metadata file", s.MetadataLoopback},
|
|
| 50 |
+ {"Data Space Used", fmt.Sprintf("%.1f Mb", float64(s.Data.Used)/(1024*1024))},
|
|
| 51 |
+ {"Data Space Total", fmt.Sprintf("%.1f Mb", float64(s.Data.Total)/(1024*1024))},
|
|
| 52 |
+ {"Metadata Space Used", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Used)/(1024*1024))},
|
|
| 53 |
+ {"Metadata Space Total", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Total)/(1024*1024))},
|
|
| 54 |
+ } |
|
| 55 |
+ return status |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func (d *Driver) Cleanup() error {
|
|
| 59 |
+ return d.DeviceSet.Shutdown() |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+func (d *Driver) Create(id, parent string) error {
|
|
| 63 |
+ if err := d.DeviceSet.AddDevice(id, parent); err != nil {
|
|
| 64 |
+ return err |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ mp := path.Join(d.home, "mnt", id) |
|
| 68 |
+ if err := d.mount(id, mp); err != nil {
|
|
| 69 |
+ return err |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ if err := osMkdirAll(path.Join(mp, "rootfs"), 0755); err != nil && !osIsExist(err) {
|
|
| 73 |
+ return err |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ // Create an "id" file with the container/image id in it to help reconscruct this in case |
|
| 77 |
+ // of later problems |
|
| 78 |
+ if err := ioutil.WriteFile(path.Join(mp, "id"), []byte(id), 0600); err != nil {
|
|
| 79 |
+ return err |
|
| 80 |
+ } |
|
| 81 |
+ |
|
| 82 |
+ // We float this reference so that the next Get call can |
|
| 83 |
+ // steal it, so we don't have to unmount |
|
| 84 |
+ if err := d.DeviceSet.UnmountDevice(id, UnmountFloat); err != nil {
|
|
| 85 |
+ return err |
|
| 86 |
+ } |
|
| 87 |
+ |
|
| 88 |
+ return nil |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+func (d *Driver) Remove(id string) error {
|
|
| 92 |
+ if !d.DeviceSet.HasDevice(id) {
|
|
| 93 |
+ // Consider removing a non-existing device a no-op |
|
| 94 |
+ // This is useful to be able to progress on container removal |
|
| 95 |
+ // if the underlying device has gone away due to earlier errors |
|
| 96 |
+ return nil |
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 99 |
+ // Sink the float from create in case no Get() call was made |
|
| 100 |
+ if err := d.DeviceSet.UnmountDevice(id, UnmountSink); err != nil {
|
|
| 101 |
+ return err |
|
| 102 |
+ } |
|
| 103 |
+ // This assumes the device has been properly Get/Put:ed and thus is unmounted |
|
| 104 |
+ if err := d.DeviceSet.DeleteDevice(id); err != nil {
|
|
| 105 |
+ return err |
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ mp := path.Join(d.home, "mnt", id) |
|
| 109 |
+ if err := os.RemoveAll(mp); err != nil && !os.IsNotExist(err) {
|
|
| 110 |
+ return err |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ return nil |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+func (d *Driver) Get(id string) (string, error) {
|
|
| 117 |
+ mp := path.Join(d.home, "mnt", id) |
|
| 118 |
+ if err := d.mount(id, mp); err != nil {
|
|
| 119 |
+ return "", err |
|
| 120 |
+ } |
|
| 121 |
+ |
|
| 122 |
+ return path.Join(mp, "rootfs"), nil |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+func (d *Driver) Put(id string) {
|
|
| 126 |
+ if err := d.DeviceSet.UnmountDevice(id, UnmountRegular); err != nil {
|
|
| 127 |
+ utils.Errorf("Warning: error unmounting device %s: %s\n", id, err)
|
|
| 128 |
+ } |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func (d *Driver) mount(id, mountPoint string) error {
|
|
| 132 |
+ // Create the target directories if they don't exist |
|
| 133 |
+ if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) {
|
|
| 134 |
+ return err |
|
| 135 |
+ } |
|
| 136 |
+ // Mount the device |
|
| 137 |
+ return d.DeviceSet.MountDevice(id, mountPoint) |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+func (d *Driver) Exists(id string) bool {
|
|
| 141 |
+ return d.Devices[id] != nil |
|
| 142 |
+} |
| 0 | 143 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,886 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "path" |
|
| 9 |
+ "runtime" |
|
| 10 |
+ "strings" |
|
| 11 |
+ "syscall" |
|
| 12 |
+ "testing" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func init() {
|
|
| 16 |
+ // Reduce the size the the base fs and loopback for the tests |
|
| 17 |
+ DefaultDataLoopbackSize = 300 * 1024 * 1024 |
|
| 18 |
+ DefaultMetaDataLoopbackSize = 200 * 1024 * 1024 |
|
| 19 |
+ DefaultBaseFsSize = 300 * 1024 * 1024 |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// denyAllDevmapper mocks all calls to libdevmapper in the unit tests, and denies them by default |
|
| 23 |
+func denyAllDevmapper() {
|
|
| 24 |
+ // Hijack all calls to libdevmapper with default panics. |
|
| 25 |
+ // Authorized calls are selectively hijacked in each tests. |
|
| 26 |
+ DmTaskCreate = func(t int) *CDmTask {
|
|
| 27 |
+ panic("DmTaskCreate: this method should not be called here")
|
|
| 28 |
+ } |
|
| 29 |
+ DmTaskRun = func(task *CDmTask) int {
|
|
| 30 |
+ panic("DmTaskRun: this method should not be called here")
|
|
| 31 |
+ } |
|
| 32 |
+ DmTaskSetName = func(task *CDmTask, name string) int {
|
|
| 33 |
+ panic("DmTaskSetName: this method should not be called here")
|
|
| 34 |
+ } |
|
| 35 |
+ DmTaskSetMessage = func(task *CDmTask, message string) int {
|
|
| 36 |
+ panic("DmTaskSetMessage: this method should not be called here")
|
|
| 37 |
+ } |
|
| 38 |
+ DmTaskSetSector = func(task *CDmTask, sector uint64) int {
|
|
| 39 |
+ panic("DmTaskSetSector: this method should not be called here")
|
|
| 40 |
+ } |
|
| 41 |
+ DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 42 |
+ panic("DmTaskSetCookie: this method should not be called here")
|
|
| 43 |
+ } |
|
| 44 |
+ DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
|
|
| 45 |
+ panic("DmTaskSetAddNode: this method should not be called here")
|
|
| 46 |
+ } |
|
| 47 |
+ DmTaskSetRo = func(task *CDmTask) int {
|
|
| 48 |
+ panic("DmTaskSetRo: this method should not be called here")
|
|
| 49 |
+ } |
|
| 50 |
+ DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
|
|
| 51 |
+ panic("DmTaskAddTarget: this method should not be called here")
|
|
| 52 |
+ } |
|
| 53 |
+ DmTaskGetInfo = func(task *CDmTask, info *Info) int {
|
|
| 54 |
+ panic("DmTaskGetInfo: this method should not be called here")
|
|
| 55 |
+ } |
|
| 56 |
+ DmGetNextTarget = func(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
|
|
| 57 |
+ panic("DmGetNextTarget: this method should not be called here")
|
|
| 58 |
+ } |
|
| 59 |
+ DmUdevWait = func(cookie uint) int {
|
|
| 60 |
+ panic("DmUdevWait: this method should not be called here")
|
|
| 61 |
+ } |
|
| 62 |
+ DmSetDevDir = func(dir string) int {
|
|
| 63 |
+ panic("DmSetDevDir: this method should not be called here")
|
|
| 64 |
+ } |
|
| 65 |
+ DmGetLibraryVersion = func(version *string) int {
|
|
| 66 |
+ panic("DmGetLibraryVersion: this method should not be called here")
|
|
| 67 |
+ } |
|
| 68 |
+ DmLogInitVerbose = func(level int) {
|
|
| 69 |
+ panic("DmLogInitVerbose: this method should not be called here")
|
|
| 70 |
+ } |
|
| 71 |
+ DmTaskDestroy = func(task *CDmTask) {
|
|
| 72 |
+ panic("DmTaskDestroy: this method should not be called here")
|
|
| 73 |
+ } |
|
| 74 |
+ LogWithErrnoInit = func() {
|
|
| 75 |
+ panic("LogWithErrnoInit: this method should not be called here")
|
|
| 76 |
+ } |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+func denyAllSyscall() {
|
|
| 80 |
+ sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
|
|
| 81 |
+ panic("sysMount: this method should not be called here")
|
|
| 82 |
+ } |
|
| 83 |
+ sysUnmount = func(target string, flags int) (err error) {
|
|
| 84 |
+ panic("sysUnmount: this method should not be called here")
|
|
| 85 |
+ } |
|
| 86 |
+ sysCloseOnExec = func(fd int) {
|
|
| 87 |
+ panic("sysCloseOnExec: this method should not be called here")
|
|
| 88 |
+ } |
|
| 89 |
+ sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
|
| 90 |
+ panic("sysSyscall: this method should not be called here")
|
|
| 91 |
+ } |
|
| 92 |
+ // Not a syscall, but forbidding it here anyway |
|
| 93 |
+ Mounted = func(mnt string) (bool, error) {
|
|
| 94 |
+ panic("devmapper.Mounted: this method should not be called here")
|
|
| 95 |
+ } |
|
| 96 |
+ // osOpenFile = os.OpenFile |
|
| 97 |
+ // osNewFile = os.NewFile |
|
| 98 |
+ // osCreate = os.Create |
|
| 99 |
+ // osStat = os.Stat |
|
| 100 |
+ // osIsNotExist = os.IsNotExist |
|
| 101 |
+ // osIsExist = os.IsExist |
|
| 102 |
+ // osMkdirAll = os.MkdirAll |
|
| 103 |
+ // osRemoveAll = os.RemoveAll |
|
| 104 |
+ // osRename = os.Rename |
|
| 105 |
+ // osReadlink = os.Readlink |
|
| 106 |
+ |
|
| 107 |
+ // execRun = func(name string, args ...string) error {
|
|
| 108 |
+ // return exec.Command(name, args...).Run() |
|
| 109 |
+ // } |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+func mkTestDirectory(t *testing.T) string {
|
|
| 113 |
+ dir, err := ioutil.TempDir("", "docker-test-devmapper-")
|
|
| 114 |
+ if err != nil {
|
|
| 115 |
+ t.Fatal(err) |
|
| 116 |
+ } |
|
| 117 |
+ return dir |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+func newDriver(t *testing.T) *Driver {
|
|
| 121 |
+ home := mkTestDirectory(t) |
|
| 122 |
+ d, err := Init(home) |
|
| 123 |
+ if err != nil {
|
|
| 124 |
+ t.Fatal(err) |
|
| 125 |
+ } |
|
| 126 |
+ return d.(*Driver) |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+func cleanup(d *Driver) {
|
|
| 130 |
+ d.Cleanup() |
|
| 131 |
+ osRemoveAll(d.home) |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+type Set map[string]bool |
|
| 135 |
+ |
|
| 136 |
+func (r Set) Assert(t *testing.T, names ...string) {
|
|
| 137 |
+ for _, key := range names {
|
|
| 138 |
+ required := true |
|
| 139 |
+ if strings.HasPrefix(key, "?") {
|
|
| 140 |
+ key = key[1:] |
|
| 141 |
+ required = false |
|
| 142 |
+ } |
|
| 143 |
+ if _, exists := r[key]; !exists && required {
|
|
| 144 |
+ t.Fatalf("Key not set: %s", key)
|
|
| 145 |
+ } |
|
| 146 |
+ delete(r, key) |
|
| 147 |
+ } |
|
| 148 |
+ if len(r) != 0 {
|
|
| 149 |
+ t.Fatalf("Unexpected keys: %v", r)
|
|
| 150 |
+ } |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+func TestInit(t *testing.T) {
|
|
| 154 |
+ var ( |
|
| 155 |
+ calls = make(Set) |
|
| 156 |
+ taskMessages = make(Set) |
|
| 157 |
+ taskTypes = make(Set) |
|
| 158 |
+ home = mkTestDirectory(t) |
|
| 159 |
+ ) |
|
| 160 |
+ defer osRemoveAll(home) |
|
| 161 |
+ |
|
| 162 |
+ func() {
|
|
| 163 |
+ denyAllDevmapper() |
|
| 164 |
+ DmSetDevDir = func(dir string) int {
|
|
| 165 |
+ calls["DmSetDevDir"] = true |
|
| 166 |
+ expectedDir := "/dev" |
|
| 167 |
+ if dir != expectedDir {
|
|
| 168 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmSetDevDir(%v)\nReceived: DmSetDevDir(%v)\n", expectedDir, dir)
|
|
| 169 |
+ } |
|
| 170 |
+ return 0 |
|
| 171 |
+ } |
|
| 172 |
+ LogWithErrnoInit = func() {
|
|
| 173 |
+ calls["DmLogWithErrnoInit"] = true |
|
| 174 |
+ } |
|
| 175 |
+ var task1 CDmTask |
|
| 176 |
+ DmTaskCreate = func(taskType int) *CDmTask {
|
|
| 177 |
+ calls["DmTaskCreate"] = true |
|
| 178 |
+ taskTypes[fmt.Sprintf("%d", taskType)] = true
|
|
| 179 |
+ return &task1 |
|
| 180 |
+ } |
|
| 181 |
+ DmTaskSetName = func(task *CDmTask, name string) int {
|
|
| 182 |
+ calls["DmTaskSetName"] = true |
|
| 183 |
+ expectedTask := &task1 |
|
| 184 |
+ if task != expectedTask {
|
|
| 185 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", expectedTask, task)
|
|
| 186 |
+ } |
|
| 187 |
+ // FIXME: use Set.AssertRegexp() |
|
| 188 |
+ if !strings.HasPrefix(name, "docker-") && !strings.HasPrefix(name, "/dev/mapper/docker-") || |
|
| 189 |
+ !strings.HasSuffix(name, "-pool") && !strings.HasSuffix(name, "-base") {
|
|
| 190 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", "docker-...-pool", name)
|
|
| 191 |
+ } |
|
| 192 |
+ return 1 |
|
| 193 |
+ } |
|
| 194 |
+ DmTaskRun = func(task *CDmTask) int {
|
|
| 195 |
+ calls["DmTaskRun"] = true |
|
| 196 |
+ expectedTask := &task1 |
|
| 197 |
+ if task != expectedTask {
|
|
| 198 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskRun(%v)\nReceived: DmTaskRun(%v)\n", expectedTask, task)
|
|
| 199 |
+ } |
|
| 200 |
+ return 1 |
|
| 201 |
+ } |
|
| 202 |
+ DmTaskGetInfo = func(task *CDmTask, info *Info) int {
|
|
| 203 |
+ calls["DmTaskGetInfo"] = true |
|
| 204 |
+ expectedTask := &task1 |
|
| 205 |
+ if task != expectedTask {
|
|
| 206 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskGetInfo(%v)\nReceived: DmTaskGetInfo(%v)\n", expectedTask, task)
|
|
| 207 |
+ } |
|
| 208 |
+ // This will crash if info is not dereferenceable |
|
| 209 |
+ info.Exists = 0 |
|
| 210 |
+ return 1 |
|
| 211 |
+ } |
|
| 212 |
+ DmTaskSetSector = func(task *CDmTask, sector uint64) int {
|
|
| 213 |
+ calls["DmTaskSetSector"] = true |
|
| 214 |
+ expectedTask := &task1 |
|
| 215 |
+ if task != expectedTask {
|
|
| 216 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task)
|
|
| 217 |
+ } |
|
| 218 |
+ if expectedSector := uint64(0); sector != expectedSector {
|
|
| 219 |
+ t.Fatalf("Wrong libdevmapper call to DmTaskSetSector\nExpected: %v\nReceived: %v\n", expectedSector, sector)
|
|
| 220 |
+ } |
|
| 221 |
+ return 1 |
|
| 222 |
+ } |
|
| 223 |
+ DmTaskSetMessage = func(task *CDmTask, message string) int {
|
|
| 224 |
+ calls["DmTaskSetMessage"] = true |
|
| 225 |
+ expectedTask := &task1 |
|
| 226 |
+ if task != expectedTask {
|
|
| 227 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task)
|
|
| 228 |
+ } |
|
| 229 |
+ taskMessages[message] = true |
|
| 230 |
+ return 1 |
|
| 231 |
+ } |
|
| 232 |
+ DmTaskDestroy = func(task *CDmTask) {
|
|
| 233 |
+ calls["DmTaskDestroy"] = true |
|
| 234 |
+ expectedTask := &task1 |
|
| 235 |
+ if task != expectedTask {
|
|
| 236 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
|
|
| 237 |
+ } |
|
| 238 |
+ } |
|
| 239 |
+ DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
|
|
| 240 |
+ calls["DmTaskSetTarget"] = true |
|
| 241 |
+ expectedTask := &task1 |
|
| 242 |
+ if task != expectedTask {
|
|
| 243 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
|
|
| 244 |
+ } |
|
| 245 |
+ if start != 0 {
|
|
| 246 |
+ t.Fatalf("Wrong start: %d != %d", start, 0)
|
|
| 247 |
+ } |
|
| 248 |
+ if ttype != "thin" && ttype != "thin-pool" {
|
|
| 249 |
+ t.Fatalf("Wrong ttype: %s", ttype)
|
|
| 250 |
+ } |
|
| 251 |
+ // Quick smoke test |
|
| 252 |
+ if params == "" {
|
|
| 253 |
+ t.Fatalf("Params should not be empty")
|
|
| 254 |
+ } |
|
| 255 |
+ return 1 |
|
| 256 |
+ } |
|
| 257 |
+ fakeCookie := uint(4321) |
|
| 258 |
+ DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 259 |
+ calls["DmTaskSetCookie"] = true |
|
| 260 |
+ expectedTask := &task1 |
|
| 261 |
+ if task != expectedTask {
|
|
| 262 |
+ t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
|
|
| 263 |
+ } |
|
| 264 |
+ if flags != 0 {
|
|
| 265 |
+ t.Fatalf("Cookie flags should be 0 (not %x)", flags)
|
|
| 266 |
+ } |
|
| 267 |
+ *cookie = fakeCookie |
|
| 268 |
+ return 1 |
|
| 269 |
+ } |
|
| 270 |
+ DmUdevWait = func(cookie uint) int {
|
|
| 271 |
+ calls["DmUdevWait"] = true |
|
| 272 |
+ if cookie != fakeCookie {
|
|
| 273 |
+ t.Fatalf("Wrong cookie: %d != %d", cookie, fakeCookie)
|
|
| 274 |
+ } |
|
| 275 |
+ return 1 |
|
| 276 |
+ } |
|
| 277 |
+ DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
|
|
| 278 |
+ if addNode != AddNodeOnCreate {
|
|
| 279 |
+ t.Fatalf("Wrong AddNoteType: %v (expected %v)", addNode, AddNodeOnCreate)
|
|
| 280 |
+ } |
|
| 281 |
+ calls["DmTaskSetAddNode"] = true |
|
| 282 |
+ return 1 |
|
| 283 |
+ } |
|
| 284 |
+ execRun = func(name string, args ...string) error {
|
|
| 285 |
+ calls["execRun"] = true |
|
| 286 |
+ if name != "mkfs.ext4" {
|
|
| 287 |
+ t.Fatalf("Expected %s to be executed, not %s", "mkfs.ext4", name)
|
|
| 288 |
+ } |
|
| 289 |
+ return nil |
|
| 290 |
+ } |
|
| 291 |
+ driver, err := Init(home) |
|
| 292 |
+ if err != nil {
|
|
| 293 |
+ t.Fatal(err) |
|
| 294 |
+ } |
|
| 295 |
+ defer func() {
|
|
| 296 |
+ if err := driver.Cleanup(); err != nil {
|
|
| 297 |
+ t.Fatal(err) |
|
| 298 |
+ } |
|
| 299 |
+ }() |
|
| 300 |
+ }() |
|
| 301 |
+ // Put all tests in a function to make sure the garbage collection will |
|
| 302 |
+ // occur. |
|
| 303 |
+ |
|
| 304 |
+ // Call GC to cleanup runtime.Finalizers |
|
| 305 |
+ runtime.GC() |
|
| 306 |
+ |
|
| 307 |
+ calls.Assert(t, |
|
| 308 |
+ "DmSetDevDir", |
|
| 309 |
+ "DmLogWithErrnoInit", |
|
| 310 |
+ "DmTaskSetName", |
|
| 311 |
+ "DmTaskRun", |
|
| 312 |
+ "DmTaskGetInfo", |
|
| 313 |
+ "DmTaskDestroy", |
|
| 314 |
+ "execRun", |
|
| 315 |
+ "DmTaskCreate", |
|
| 316 |
+ "DmTaskSetTarget", |
|
| 317 |
+ "DmTaskSetCookie", |
|
| 318 |
+ "DmUdevWait", |
|
| 319 |
+ "DmTaskSetSector", |
|
| 320 |
+ "DmTaskSetMessage", |
|
| 321 |
+ "DmTaskSetAddNode", |
|
| 322 |
+ ) |
|
| 323 |
+ taskTypes.Assert(t, "0", "6", "17") |
|
| 324 |
+ taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1") |
|
| 325 |
+} |
|
| 326 |
+ |
|
| 327 |
+func fakeInit() func(home string) (graphdriver.Driver, error) {
|
|
| 328 |
+ oldInit := Init |
|
| 329 |
+ Init = func(home string) (graphdriver.Driver, error) {
|
|
| 330 |
+ return &Driver{
|
|
| 331 |
+ home: home, |
|
| 332 |
+ }, nil |
|
| 333 |
+ } |
|
| 334 |
+ return oldInit |
|
| 335 |
+} |
|
| 336 |
+ |
|
| 337 |
+func restoreInit(init func(home string) (graphdriver.Driver, error)) {
|
|
| 338 |
+ Init = init |
|
| 339 |
+} |
|
| 340 |
+ |
|
| 341 |
+func mockAllDevmapper(calls Set) {
|
|
| 342 |
+ DmSetDevDir = func(dir string) int {
|
|
| 343 |
+ calls["DmSetDevDir"] = true |
|
| 344 |
+ return 0 |
|
| 345 |
+ } |
|
| 346 |
+ LogWithErrnoInit = func() {
|
|
| 347 |
+ calls["DmLogWithErrnoInit"] = true |
|
| 348 |
+ } |
|
| 349 |
+ DmTaskCreate = func(taskType int) *CDmTask {
|
|
| 350 |
+ calls["DmTaskCreate"] = true |
|
| 351 |
+ return &CDmTask{}
|
|
| 352 |
+ } |
|
| 353 |
+ DmTaskSetName = func(task *CDmTask, name string) int {
|
|
| 354 |
+ calls["DmTaskSetName"] = true |
|
| 355 |
+ return 1 |
|
| 356 |
+ } |
|
| 357 |
+ DmTaskRun = func(task *CDmTask) int {
|
|
| 358 |
+ calls["DmTaskRun"] = true |
|
| 359 |
+ return 1 |
|
| 360 |
+ } |
|
| 361 |
+ DmTaskGetInfo = func(task *CDmTask, info *Info) int {
|
|
| 362 |
+ calls["DmTaskGetInfo"] = true |
|
| 363 |
+ return 1 |
|
| 364 |
+ } |
|
| 365 |
+ DmTaskSetSector = func(task *CDmTask, sector uint64) int {
|
|
| 366 |
+ calls["DmTaskSetSector"] = true |
|
| 367 |
+ return 1 |
|
| 368 |
+ } |
|
| 369 |
+ DmTaskSetMessage = func(task *CDmTask, message string) int {
|
|
| 370 |
+ calls["DmTaskSetMessage"] = true |
|
| 371 |
+ return 1 |
|
| 372 |
+ } |
|
| 373 |
+ DmTaskDestroy = func(task *CDmTask) {
|
|
| 374 |
+ calls["DmTaskDestroy"] = true |
|
| 375 |
+ } |
|
| 376 |
+ DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
|
|
| 377 |
+ calls["DmTaskSetTarget"] = true |
|
| 378 |
+ return 1 |
|
| 379 |
+ } |
|
| 380 |
+ DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
|
|
| 381 |
+ calls["DmTaskSetCookie"] = true |
|
| 382 |
+ return 1 |
|
| 383 |
+ } |
|
| 384 |
+ DmUdevWait = func(cookie uint) int {
|
|
| 385 |
+ calls["DmUdevWait"] = true |
|
| 386 |
+ return 1 |
|
| 387 |
+ } |
|
| 388 |
+ DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
|
|
| 389 |
+ calls["DmTaskSetAddNode"] = true |
|
| 390 |
+ return 1 |
|
| 391 |
+ } |
|
| 392 |
+ execRun = func(name string, args ...string) error {
|
|
| 393 |
+ calls["execRun"] = true |
|
| 394 |
+ return nil |
|
| 395 |
+ } |
|
| 396 |
+} |
|
| 397 |
+ |
|
| 398 |
+func TestDriverName(t *testing.T) {
|
|
| 399 |
+ denyAllDevmapper() |
|
| 400 |
+ defer denyAllDevmapper() |
|
| 401 |
+ |
|
| 402 |
+ oldInit := fakeInit() |
|
| 403 |
+ defer restoreInit(oldInit) |
|
| 404 |
+ |
|
| 405 |
+ d := newDriver(t) |
|
| 406 |
+ if d.String() != "devicemapper" {
|
|
| 407 |
+ t.Fatalf("Expected driver name to be devicemapper got %s", d.String())
|
|
| 408 |
+ } |
|
| 409 |
+} |
|
| 410 |
+ |
|
| 411 |
+func TestDriverCreate(t *testing.T) {
|
|
| 412 |
+ denyAllDevmapper() |
|
| 413 |
+ denyAllSyscall() |
|
| 414 |
+ defer denyAllSyscall() |
|
| 415 |
+ defer denyAllDevmapper() |
|
| 416 |
+ |
|
| 417 |
+ calls := make(Set) |
|
| 418 |
+ mockAllDevmapper(calls) |
|
| 419 |
+ |
|
| 420 |
+ sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
|
|
| 421 |
+ calls["sysMount"] = true |
|
| 422 |
+ // FIXME: compare the exact source and target strings (inodes + devname) |
|
| 423 |
+ if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) {
|
|
| 424 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source)
|
|
| 425 |
+ } |
|
| 426 |
+ if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
|
|
| 427 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
|
|
| 428 |
+ } |
|
| 429 |
+ if expectedFstype := "ext4"; fstype != expectedFstype {
|
|
| 430 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype)
|
|
| 431 |
+ } |
|
| 432 |
+ if expectedFlags := uintptr(3236757504); flags != expectedFlags {
|
|
| 433 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
|
|
| 434 |
+ } |
|
| 435 |
+ return nil |
|
| 436 |
+ } |
|
| 437 |
+ |
|
| 438 |
+ Mounted = func(mnt string) (bool, error) {
|
|
| 439 |
+ calls["Mounted"] = true |
|
| 440 |
+ if !strings.HasPrefix(mnt, "/tmp/docker-test-devmapper-") || !strings.HasSuffix(mnt, "/mnt/1") {
|
|
| 441 |
+ t.Fatalf("Wrong mounted call\nExpected: Mounted(%v)\nReceived: Mounted(%v)\n", "/tmp/docker-test-devmapper-.../mnt/1", mnt)
|
|
| 442 |
+ } |
|
| 443 |
+ return false, nil |
|
| 444 |
+ } |
|
| 445 |
+ |
|
| 446 |
+ sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
|
| 447 |
+ calls["sysSyscall"] = true |
|
| 448 |
+ if trap != sysSysIoctl {
|
|
| 449 |
+ t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap)
|
|
| 450 |
+ } |
|
| 451 |
+ switch a2 {
|
|
| 452 |
+ case LoopSetFd: |
|
| 453 |
+ calls["ioctl.loopsetfd"] = true |
|
| 454 |
+ case LoopCtlGetFree: |
|
| 455 |
+ calls["ioctl.loopctlgetfree"] = true |
|
| 456 |
+ case LoopGetStatus64: |
|
| 457 |
+ calls["ioctl.loopgetstatus"] = true |
|
| 458 |
+ case LoopSetStatus64: |
|
| 459 |
+ calls["ioctl.loopsetstatus"] = true |
|
| 460 |
+ case LoopClrFd: |
|
| 461 |
+ calls["ioctl.loopclrfd"] = true |
|
| 462 |
+ case LoopSetCapacity: |
|
| 463 |
+ calls["ioctl.loopsetcapacity"] = true |
|
| 464 |
+ case BlkGetSize64: |
|
| 465 |
+ calls["ioctl.blkgetsize"] = true |
|
| 466 |
+ default: |
|
| 467 |
+ t.Fatalf("Unexpected IOCTL. Received %d", a2)
|
|
| 468 |
+ } |
|
| 469 |
+ return 0, 0, 0 |
|
| 470 |
+ } |
|
| 471 |
+ |
|
| 472 |
+ func() {
|
|
| 473 |
+ d := newDriver(t) |
|
| 474 |
+ |
|
| 475 |
+ calls.Assert(t, |
|
| 476 |
+ "DmSetDevDir", |
|
| 477 |
+ "DmLogWithErrnoInit", |
|
| 478 |
+ "DmTaskSetName", |
|
| 479 |
+ "DmTaskRun", |
|
| 480 |
+ "DmTaskGetInfo", |
|
| 481 |
+ "execRun", |
|
| 482 |
+ "DmTaskCreate", |
|
| 483 |
+ "DmTaskSetTarget", |
|
| 484 |
+ "DmTaskSetCookie", |
|
| 485 |
+ "DmUdevWait", |
|
| 486 |
+ "DmTaskSetSector", |
|
| 487 |
+ "DmTaskSetMessage", |
|
| 488 |
+ "DmTaskSetAddNode", |
|
| 489 |
+ "sysSyscall", |
|
| 490 |
+ "ioctl.blkgetsize", |
|
| 491 |
+ "ioctl.loopsetfd", |
|
| 492 |
+ "ioctl.loopsetstatus", |
|
| 493 |
+ "?ioctl.loopctlgetfree", |
|
| 494 |
+ ) |
|
| 495 |
+ |
|
| 496 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 497 |
+ t.Fatal(err) |
|
| 498 |
+ } |
|
| 499 |
+ calls.Assert(t, |
|
| 500 |
+ "DmTaskCreate", |
|
| 501 |
+ "DmTaskGetInfo", |
|
| 502 |
+ "sysMount", |
|
| 503 |
+ "DmTaskRun", |
|
| 504 |
+ "DmTaskSetTarget", |
|
| 505 |
+ "DmTaskSetSector", |
|
| 506 |
+ "DmTaskSetCookie", |
|
| 507 |
+ "DmUdevWait", |
|
| 508 |
+ "DmTaskSetName", |
|
| 509 |
+ "DmTaskSetMessage", |
|
| 510 |
+ "DmTaskSetAddNode", |
|
| 511 |
+ ) |
|
| 512 |
+ |
|
| 513 |
+ }() |
|
| 514 |
+ |
|
| 515 |
+ runtime.GC() |
|
| 516 |
+ |
|
| 517 |
+ calls.Assert(t, |
|
| 518 |
+ "DmTaskDestroy", |
|
| 519 |
+ ) |
|
| 520 |
+} |
|
| 521 |
+ |
|
| 522 |
+func TestDriverRemove(t *testing.T) {
|
|
| 523 |
+ denyAllDevmapper() |
|
| 524 |
+ denyAllSyscall() |
|
| 525 |
+ defer denyAllSyscall() |
|
| 526 |
+ defer denyAllDevmapper() |
|
| 527 |
+ |
|
| 528 |
+ calls := make(Set) |
|
| 529 |
+ mockAllDevmapper(calls) |
|
| 530 |
+ |
|
| 531 |
+ sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
|
|
| 532 |
+ calls["sysMount"] = true |
|
| 533 |
+ // FIXME: compare the exact source and target strings (inodes + devname) |
|
| 534 |
+ if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) {
|
|
| 535 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source)
|
|
| 536 |
+ } |
|
| 537 |
+ if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
|
|
| 538 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
|
|
| 539 |
+ } |
|
| 540 |
+ if expectedFstype := "ext4"; fstype != expectedFstype {
|
|
| 541 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype)
|
|
| 542 |
+ } |
|
| 543 |
+ if expectedFlags := uintptr(3236757504); flags != expectedFlags {
|
|
| 544 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
|
|
| 545 |
+ } |
|
| 546 |
+ return nil |
|
| 547 |
+ } |
|
| 548 |
+ sysUnmount = func(target string, flags int) (err error) {
|
|
| 549 |
+ calls["sysUnmount"] = true |
|
| 550 |
+ // FIXME: compare the exact source and target strings (inodes + devname) |
|
| 551 |
+ if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
|
|
| 552 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
|
|
| 553 |
+ } |
|
| 554 |
+ if expectedFlags := 0; flags != expectedFlags {
|
|
| 555 |
+ t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
|
|
| 556 |
+ } |
|
| 557 |
+ return nil |
|
| 558 |
+ } |
|
| 559 |
+ Mounted = func(mnt string) (bool, error) {
|
|
| 560 |
+ calls["Mounted"] = true |
|
| 561 |
+ return false, nil |
|
| 562 |
+ } |
|
| 563 |
+ |
|
| 564 |
+ sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
|
| 565 |
+ calls["sysSyscall"] = true |
|
| 566 |
+ if trap != sysSysIoctl {
|
|
| 567 |
+ t.Fatalf("Unexpected syscall. Expecting SYS_IOCTL, received: %d", trap)
|
|
| 568 |
+ } |
|
| 569 |
+ switch a2 {
|
|
| 570 |
+ case LoopSetFd: |
|
| 571 |
+ calls["ioctl.loopsetfd"] = true |
|
| 572 |
+ case LoopCtlGetFree: |
|
| 573 |
+ calls["ioctl.loopctlgetfree"] = true |
|
| 574 |
+ case LoopGetStatus64: |
|
| 575 |
+ calls["ioctl.loopgetstatus"] = true |
|
| 576 |
+ case LoopSetStatus64: |
|
| 577 |
+ calls["ioctl.loopsetstatus"] = true |
|
| 578 |
+ case LoopClrFd: |
|
| 579 |
+ calls["ioctl.loopclrfd"] = true |
|
| 580 |
+ case LoopSetCapacity: |
|
| 581 |
+ calls["ioctl.loopsetcapacity"] = true |
|
| 582 |
+ case BlkGetSize64: |
|
| 583 |
+ calls["ioctl.blkgetsize"] = true |
|
| 584 |
+ default: |
|
| 585 |
+ t.Fatalf("Unexpected IOCTL. Received %d", a2)
|
|
| 586 |
+ } |
|
| 587 |
+ return 0, 0, 0 |
|
| 588 |
+ } |
|
| 589 |
+ |
|
| 590 |
+ func() {
|
|
| 591 |
+ d := newDriver(t) |
|
| 592 |
+ |
|
| 593 |
+ calls.Assert(t, |
|
| 594 |
+ "DmSetDevDir", |
|
| 595 |
+ "DmLogWithErrnoInit", |
|
| 596 |
+ "DmTaskSetName", |
|
| 597 |
+ "DmTaskRun", |
|
| 598 |
+ "DmTaskGetInfo", |
|
| 599 |
+ "execRun", |
|
| 600 |
+ "DmTaskCreate", |
|
| 601 |
+ "DmTaskSetTarget", |
|
| 602 |
+ "DmTaskSetCookie", |
|
| 603 |
+ "DmUdevWait", |
|
| 604 |
+ "DmTaskSetSector", |
|
| 605 |
+ "DmTaskSetMessage", |
|
| 606 |
+ "DmTaskSetAddNode", |
|
| 607 |
+ "sysSyscall", |
|
| 608 |
+ "ioctl.blkgetsize", |
|
| 609 |
+ "ioctl.loopsetfd", |
|
| 610 |
+ "ioctl.loopsetstatus", |
|
| 611 |
+ "?ioctl.loopctlgetfree", |
|
| 612 |
+ ) |
|
| 613 |
+ |
|
| 614 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 615 |
+ t.Fatal(err) |
|
| 616 |
+ } |
|
| 617 |
+ |
|
| 618 |
+ calls.Assert(t, |
|
| 619 |
+ "DmTaskCreate", |
|
| 620 |
+ "DmTaskGetInfo", |
|
| 621 |
+ "sysMount", |
|
| 622 |
+ "DmTaskRun", |
|
| 623 |
+ "DmTaskSetTarget", |
|
| 624 |
+ "DmTaskSetSector", |
|
| 625 |
+ "DmTaskSetCookie", |
|
| 626 |
+ "DmUdevWait", |
|
| 627 |
+ "DmTaskSetName", |
|
| 628 |
+ "DmTaskSetMessage", |
|
| 629 |
+ "DmTaskSetAddNode", |
|
| 630 |
+ ) |
|
| 631 |
+ |
|
| 632 |
+ Mounted = func(mnt string) (bool, error) {
|
|
| 633 |
+ calls["Mounted"] = true |
|
| 634 |
+ return true, nil |
|
| 635 |
+ } |
|
| 636 |
+ |
|
| 637 |
+ if err := d.Remove("1"); err != nil {
|
|
| 638 |
+ t.Fatal(err) |
|
| 639 |
+ } |
|
| 640 |
+ |
|
| 641 |
+ calls.Assert(t, |
|
| 642 |
+ "DmTaskRun", |
|
| 643 |
+ "DmTaskSetSector", |
|
| 644 |
+ "DmTaskSetName", |
|
| 645 |
+ "DmTaskSetMessage", |
|
| 646 |
+ "DmTaskCreate", |
|
| 647 |
+ "DmTaskGetInfo", |
|
| 648 |
+ "DmTaskSetCookie", |
|
| 649 |
+ "DmTaskSetTarget", |
|
| 650 |
+ "DmTaskSetAddNode", |
|
| 651 |
+ "DmUdevWait", |
|
| 652 |
+ "sysUnmount", |
|
| 653 |
+ ) |
|
| 654 |
+ }() |
|
| 655 |
+ runtime.GC() |
|
| 656 |
+ |
|
| 657 |
+ calls.Assert(t, |
|
| 658 |
+ "DmTaskDestroy", |
|
| 659 |
+ ) |
|
| 660 |
+} |
|
| 661 |
+ |
|
| 662 |
+func TestCleanup(t *testing.T) {
|
|
| 663 |
+ t.Skip("FIXME: not a unit test")
|
|
| 664 |
+ t.Skip("Unimplemented")
|
|
| 665 |
+ d := newDriver(t) |
|
| 666 |
+ defer osRemoveAll(d.home) |
|
| 667 |
+ |
|
| 668 |
+ mountPoints := make([]string, 2) |
|
| 669 |
+ |
|
| 670 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 671 |
+ t.Fatal(err) |
|
| 672 |
+ } |
|
| 673 |
+ // Mount the id |
|
| 674 |
+ p, err := d.Get("1")
|
|
| 675 |
+ if err != nil {
|
|
| 676 |
+ t.Fatal(err) |
|
| 677 |
+ } |
|
| 678 |
+ mountPoints[0] = p |
|
| 679 |
+ |
|
| 680 |
+ if err := d.Create("2", "1"); err != nil {
|
|
| 681 |
+ t.Fatal(err) |
|
| 682 |
+ } |
|
| 683 |
+ |
|
| 684 |
+ p, err = d.Get("2")
|
|
| 685 |
+ if err != nil {
|
|
| 686 |
+ t.Fatal(err) |
|
| 687 |
+ } |
|
| 688 |
+ mountPoints[1] = p |
|
| 689 |
+ |
|
| 690 |
+ // Ensure that all the mount points are currently mounted |
|
| 691 |
+ for _, p := range mountPoints {
|
|
| 692 |
+ if mounted, err := Mounted(p); err != nil {
|
|
| 693 |
+ t.Fatal(err) |
|
| 694 |
+ } else if !mounted {
|
|
| 695 |
+ t.Fatalf("Expected %s to be mounted", p)
|
|
| 696 |
+ } |
|
| 697 |
+ } |
|
| 698 |
+ |
|
| 699 |
+ // Ensure that devices are active |
|
| 700 |
+ for _, p := range []string{"1", "2"} {
|
|
| 701 |
+ if !d.HasActivatedDevice(p) {
|
|
| 702 |
+ t.Fatalf("Expected %s to have an active device", p)
|
|
| 703 |
+ } |
|
| 704 |
+ } |
|
| 705 |
+ |
|
| 706 |
+ if err := d.Cleanup(); err != nil {
|
|
| 707 |
+ t.Fatal(err) |
|
| 708 |
+ } |
|
| 709 |
+ |
|
| 710 |
+ // Ensure that all the mount points are no longer mounted |
|
| 711 |
+ for _, p := range mountPoints {
|
|
| 712 |
+ if mounted, err := Mounted(p); err != nil {
|
|
| 713 |
+ t.Fatal(err) |
|
| 714 |
+ } else if mounted {
|
|
| 715 |
+ t.Fatalf("Expected %s to not be mounted", p)
|
|
| 716 |
+ } |
|
| 717 |
+ } |
|
| 718 |
+ |
|
| 719 |
+ // Ensure that devices are no longer activated |
|
| 720 |
+ for _, p := range []string{"1", "2"} {
|
|
| 721 |
+ if d.HasActivatedDevice(p) {
|
|
| 722 |
+ t.Fatalf("Expected %s not be an active device", p)
|
|
| 723 |
+ } |
|
| 724 |
+ } |
|
| 725 |
+} |
|
| 726 |
+ |
|
| 727 |
+func TestNotMounted(t *testing.T) {
|
|
| 728 |
+ t.Skip("FIXME: not a unit test")
|
|
| 729 |
+ t.Skip("Not implemented")
|
|
| 730 |
+ d := newDriver(t) |
|
| 731 |
+ defer cleanup(d) |
|
| 732 |
+ |
|
| 733 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 734 |
+ t.Fatal(err) |
|
| 735 |
+ } |
|
| 736 |
+ |
|
| 737 |
+ mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 738 |
+ if err != nil {
|
|
| 739 |
+ t.Fatal(err) |
|
| 740 |
+ } |
|
| 741 |
+ if mounted {
|
|
| 742 |
+ t.Fatal("Id 1 should not be mounted")
|
|
| 743 |
+ } |
|
| 744 |
+} |
|
| 745 |
+ |
|
| 746 |
+func TestMounted(t *testing.T) {
|
|
| 747 |
+ t.Skip("FIXME: not a unit test")
|
|
| 748 |
+ d := newDriver(t) |
|
| 749 |
+ defer cleanup(d) |
|
| 750 |
+ |
|
| 751 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 752 |
+ t.Fatal(err) |
|
| 753 |
+ } |
|
| 754 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 755 |
+ t.Fatal(err) |
|
| 756 |
+ } |
|
| 757 |
+ |
|
| 758 |
+ mounted, err := Mounted(path.Join(d.home, "mnt", "1")) |
|
| 759 |
+ if err != nil {
|
|
| 760 |
+ t.Fatal(err) |
|
| 761 |
+ } |
|
| 762 |
+ if !mounted {
|
|
| 763 |
+ t.Fatal("Id 1 should be mounted")
|
|
| 764 |
+ } |
|
| 765 |
+} |
|
| 766 |
+ |
|
| 767 |
+func TestInitCleanedDriver(t *testing.T) {
|
|
| 768 |
+ t.Skip("FIXME: not a unit test")
|
|
| 769 |
+ d := newDriver(t) |
|
| 770 |
+ |
|
| 771 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 772 |
+ t.Fatal(err) |
|
| 773 |
+ } |
|
| 774 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 775 |
+ t.Fatal(err) |
|
| 776 |
+ } |
|
| 777 |
+ |
|
| 778 |
+ if err := d.Cleanup(); err != nil {
|
|
| 779 |
+ t.Fatal(err) |
|
| 780 |
+ } |
|
| 781 |
+ |
|
| 782 |
+ driver, err := Init(d.home) |
|
| 783 |
+ if err != nil {
|
|
| 784 |
+ t.Fatal(err) |
|
| 785 |
+ } |
|
| 786 |
+ d = driver.(*Driver) |
|
| 787 |
+ defer cleanup(d) |
|
| 788 |
+ |
|
| 789 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 790 |
+ t.Fatal(err) |
|
| 791 |
+ } |
|
| 792 |
+} |
|
| 793 |
+ |
|
| 794 |
+func TestMountMountedDriver(t *testing.T) {
|
|
| 795 |
+ t.Skip("FIXME: not a unit test")
|
|
| 796 |
+ d := newDriver(t) |
|
| 797 |
+ defer cleanup(d) |
|
| 798 |
+ |
|
| 799 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 800 |
+ t.Fatal(err) |
|
| 801 |
+ } |
|
| 802 |
+ |
|
| 803 |
+ // Perform get on same id to ensure that it will |
|
| 804 |
+ // not be mounted twice |
|
| 805 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 806 |
+ t.Fatal(err) |
|
| 807 |
+ } |
|
| 808 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 809 |
+ t.Fatal(err) |
|
| 810 |
+ } |
|
| 811 |
+} |
|
| 812 |
+ |
|
| 813 |
+func TestGetReturnsValidDevice(t *testing.T) {
|
|
| 814 |
+ t.Skip("FIXME: not a unit test")
|
|
| 815 |
+ d := newDriver(t) |
|
| 816 |
+ defer cleanup(d) |
|
| 817 |
+ |
|
| 818 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 819 |
+ t.Fatal(err) |
|
| 820 |
+ } |
|
| 821 |
+ |
|
| 822 |
+ if !d.HasDevice("1") {
|
|
| 823 |
+ t.Fatalf("Expected id 1 to be in device set")
|
|
| 824 |
+ } |
|
| 825 |
+ |
|
| 826 |
+ if _, err := d.Get("1"); err != nil {
|
|
| 827 |
+ t.Fatal(err) |
|
| 828 |
+ } |
|
| 829 |
+ |
|
| 830 |
+ if !d.HasActivatedDevice("1") {
|
|
| 831 |
+ t.Fatalf("Expected id 1 to be activated")
|
|
| 832 |
+ } |
|
| 833 |
+ |
|
| 834 |
+ if !d.HasInitializedDevice("1") {
|
|
| 835 |
+ t.Fatalf("Expected id 1 to be initialized")
|
|
| 836 |
+ } |
|
| 837 |
+} |
|
| 838 |
+ |
|
| 839 |
+func TestDriverGetSize(t *testing.T) {
|
|
| 840 |
+ t.Skip("FIXME: not a unit test")
|
|
| 841 |
+ t.Skipf("Size is currently not implemented")
|
|
| 842 |
+ |
|
| 843 |
+ d := newDriver(t) |
|
| 844 |
+ defer cleanup(d) |
|
| 845 |
+ |
|
| 846 |
+ if err := d.Create("1", ""); err != nil {
|
|
| 847 |
+ t.Fatal(err) |
|
| 848 |
+ } |
|
| 849 |
+ |
|
| 850 |
+ mountPoint, err := d.Get("1")
|
|
| 851 |
+ if err != nil {
|
|
| 852 |
+ t.Fatal(err) |
|
| 853 |
+ } |
|
| 854 |
+ |
|
| 855 |
+ size := int64(1024) |
|
| 856 |
+ |
|
| 857 |
+ f, err := osCreate(path.Join(mountPoint, "test_file")) |
|
| 858 |
+ if err != nil {
|
|
| 859 |
+ t.Fatal(err) |
|
| 860 |
+ } |
|
| 861 |
+ if err := f.Truncate(size); err != nil {
|
|
| 862 |
+ t.Fatal(err) |
|
| 863 |
+ } |
|
| 864 |
+ f.Close() |
|
| 865 |
+ |
|
| 866 |
+ // diffSize, err := d.DiffSize("1")
|
|
| 867 |
+ // if err != nil {
|
|
| 868 |
+ // t.Fatal(err) |
|
| 869 |
+ // } |
|
| 870 |
+ // if diffSize != size {
|
|
| 871 |
+ // t.Fatalf("Expected size %d got %d", size, diffSize)
|
|
| 872 |
+ // } |
|
| 873 |
+} |
|
| 874 |
+ |
|
| 875 |
+func assertMap(t *testing.T, m map[string]bool, keys ...string) {
|
|
| 876 |
+ for _, key := range keys {
|
|
| 877 |
+ if _, exists := m[key]; !exists {
|
|
| 878 |
+ t.Fatalf("Key not set: %s", key)
|
|
| 879 |
+ } |
|
| 880 |
+ delete(m, key) |
|
| 881 |
+ } |
|
| 882 |
+ if len(m) != 0 {
|
|
| 883 |
+ t.Fatalf("Unexpected keys: %v", m)
|
|
| 884 |
+ } |
|
| 885 |
+} |
| 0 | 886 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,71 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "unsafe" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func ioctlLoopCtlGetFree(fd uintptr) (int, error) {
|
|
| 9 |
+ index, _, err := sysSyscall(sysSysIoctl, fd, LoopCtlGetFree, 0) |
|
| 10 |
+ if err != 0 {
|
|
| 11 |
+ return 0, err |
|
| 12 |
+ } |
|
| 13 |
+ return int(index), nil |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+func ioctlLoopSetFd(loopFd, sparseFd uintptr) error {
|
|
| 17 |
+ if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetFd, sparseFd); err != 0 {
|
|
| 18 |
+ return err |
|
| 19 |
+ } |
|
| 20 |
+ return nil |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *LoopInfo64) error {
|
|
| 24 |
+ if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
|
|
| 25 |
+ return err |
|
| 26 |
+ } |
|
| 27 |
+ return nil |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func ioctlLoopClrFd(loopFd uintptr) error {
|
|
| 31 |
+ if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopClrFd, 0); err != 0 {
|
|
| 32 |
+ return err |
|
| 33 |
+ } |
|
| 34 |
+ return nil |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func ioctlLoopGetStatus64(loopFd uintptr) (*LoopInfo64, error) {
|
|
| 38 |
+ loopInfo := &LoopInfo64{}
|
|
| 39 |
+ |
|
| 40 |
+ if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 {
|
|
| 41 |
+ return nil, err |
|
| 42 |
+ } |
|
| 43 |
+ return loopInfo, nil |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func ioctlLoopSetCapacity(loopFd uintptr, value int) error {
|
|
| 47 |
+ if _, _, err := sysSyscall(sysSysIoctl, loopFd, LoopSetCapacity, uintptr(value)); err != 0 {
|
|
| 48 |
+ return err |
|
| 49 |
+ } |
|
| 50 |
+ return nil |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func ioctlBlkGetSize64(fd uintptr) (int64, error) {
|
|
| 54 |
+ var size int64 |
|
| 55 |
+ if _, _, err := sysSyscall(sysSysIoctl, fd, BlkGetSize64, uintptr(unsafe.Pointer(&size))); err != 0 {
|
|
| 56 |
+ return 0, err |
|
| 57 |
+ } |
|
| 58 |
+ return size, nil |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func ioctlBlkDiscard(fd uintptr, offset, length uint64) error {
|
|
| 62 |
+ var r [2]uint64 |
|
| 63 |
+ r[0] = offset |
|
| 64 |
+ r[1] = length |
|
| 65 |
+ |
|
| 66 |
+ if _, _, err := sysSyscall(sysSysIoctl, fd, BlkDiscard, uintptr(unsafe.Pointer(&r[0]))); err != 0 {
|
|
| 67 |
+ return err |
|
| 68 |
+ } |
|
| 69 |
+ return nil |
|
| 70 |
+} |
| 0 | 71 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "path/filepath" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// FIXME: this is copy-pasted from the aufs driver. |
|
| 9 |
+// It should be moved into the core. |
|
| 10 |
+ |
|
| 11 |
+var Mounted = func(mountpoint string) (bool, error) {
|
|
| 12 |
+ mntpoint, err := osStat(mountpoint) |
|
| 13 |
+ if err != nil {
|
|
| 14 |
+ if osIsNotExist(err) {
|
|
| 15 |
+ return false, nil |
|
| 16 |
+ } |
|
| 17 |
+ return false, err |
|
| 18 |
+ } |
|
| 19 |
+ parent, err := osStat(filepath.Join(mountpoint, "..")) |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ return false, err |
|
| 22 |
+ } |
|
| 23 |
+ mntpointSt := toSysStatT(mntpoint.Sys()) |
|
| 24 |
+ parentSt := toSysStatT(parent.Sys()) |
|
| 25 |
+ return mntpointSt.Dev != parentSt.Dev, nil |
|
| 26 |
+} |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,57 @@ |
| 0 |
+// +build linux,amd64 |
|
| 1 |
+ |
|
| 2 |
+package devmapper |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "os" |
|
| 6 |
+ "os/exec" |
|
| 7 |
+ "syscall" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type ( |
|
| 11 |
+ sysStatT syscall.Stat_t |
|
| 12 |
+ sysErrno syscall.Errno |
|
| 13 |
+ |
|
| 14 |
+ osFile struct{ *os.File }
|
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+var ( |
|
| 18 |
+ sysMount = syscall.Mount |
|
| 19 |
+ sysUnmount = syscall.Unmount |
|
| 20 |
+ sysCloseOnExec = syscall.CloseOnExec |
|
| 21 |
+ sysSyscall = syscall.Syscall |
|
| 22 |
+ |
|
| 23 |
+ osOpenFile = func(name string, flag int, perm os.FileMode) (*osFile, error) {
|
|
| 24 |
+ f, err := os.OpenFile(name, flag, perm) |
|
| 25 |
+ return &osFile{File: f}, err
|
|
| 26 |
+ } |
|
| 27 |
+ osOpen = func(name string) (*osFile, error) { f, err := os.Open(name); return &osFile{File: f}, err }
|
|
| 28 |
+ osNewFile = os.NewFile |
|
| 29 |
+ osCreate = os.Create |
|
| 30 |
+ osStat = os.Stat |
|
| 31 |
+ osIsNotExist = os.IsNotExist |
|
| 32 |
+ osIsExist = os.IsExist |
|
| 33 |
+ osMkdirAll = os.MkdirAll |
|
| 34 |
+ osRemoveAll = os.RemoveAll |
|
| 35 |
+ osRename = os.Rename |
|
| 36 |
+ osReadlink = os.Readlink |
|
| 37 |
+ |
|
| 38 |
+ execRun = func(name string, args ...string) error { return exec.Command(name, args...).Run() }
|
|
| 39 |
+) |
|
| 40 |
+ |
|
| 41 |
+const ( |
|
| 42 |
+ sysMsMgcVal = syscall.MS_MGC_VAL |
|
| 43 |
+ sysMsRdOnly = syscall.MS_RDONLY |
|
| 44 |
+ sysEInval = syscall.EINVAL |
|
| 45 |
+ sysSysIoctl = syscall.SYS_IOCTL |
|
| 46 |
+ sysEBusy = syscall.EBUSY |
|
| 47 |
+ |
|
| 48 |
+ osORdOnly = os.O_RDONLY |
|
| 49 |
+ osORdWr = os.O_RDWR |
|
| 50 |
+ osOCreate = os.O_CREATE |
|
| 51 |
+ osModeDevice = os.ModeDevice |
|
| 52 |
+) |
|
| 53 |
+ |
|
| 54 |
+func toSysStatT(i interface{}) *sysStatT {
|
|
| 55 |
+ return (*sysStatT)(i.(*syscall.Stat_t)) |
|
| 56 |
+} |
| 0 | 57 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,93 @@ |
| 0 |
+package graphdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/archive" |
|
| 5 |
+ "github.com/dotcloud/docker/utils" |
|
| 6 |
+ "os" |
|
| 7 |
+ "path" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type InitFunc func(root string) (Driver, error) |
|
| 11 |
+ |
|
| 12 |
+type Driver interface {
|
|
| 13 |
+ String() string |
|
| 14 |
+ |
|
| 15 |
+ Create(id, parent string) error |
|
| 16 |
+ Remove(id string) error |
|
| 17 |
+ |
|
| 18 |
+ Get(id string) (dir string, err error) |
|
| 19 |
+ Put(id string) |
|
| 20 |
+ Exists(id string) bool |
|
| 21 |
+ |
|
| 22 |
+ Status() [][2]string |
|
| 23 |
+ |
|
| 24 |
+ Cleanup() error |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+type Differ interface {
|
|
| 28 |
+ Diff(id string) (archive.Archive, error) |
|
| 29 |
+ Changes(id string) ([]archive.Change, error) |
|
| 30 |
+ ApplyDiff(id string, diff archive.ArchiveReader) error |
|
| 31 |
+ DiffSize(id string) (bytes int64, err error) |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+var ( |
|
| 35 |
+ DefaultDriver string |
|
| 36 |
+ // All registred drivers |
|
| 37 |
+ drivers map[string]InitFunc |
|
| 38 |
+ // Slice of drivers that should be used in an order |
|
| 39 |
+ priority = []string{
|
|
| 40 |
+ "aufs", |
|
| 41 |
+ "devicemapper", |
|
| 42 |
+ "vfs", |
|
| 43 |
+ // experimental, has to be enabled manually for now |
|
| 44 |
+ "btrfs", |
|
| 45 |
+ } |
|
| 46 |
+) |
|
| 47 |
+ |
|
| 48 |
+func init() {
|
|
| 49 |
+ drivers = make(map[string]InitFunc) |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func Register(name string, initFunc InitFunc) error {
|
|
| 53 |
+ if _, exists := drivers[name]; exists {
|
|
| 54 |
+ return fmt.Errorf("Name already registered %s", name)
|
|
| 55 |
+ } |
|
| 56 |
+ drivers[name] = initFunc |
|
| 57 |
+ |
|
| 58 |
+ return nil |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func GetDriver(name, home string) (Driver, error) {
|
|
| 62 |
+ if initFunc, exists := drivers[name]; exists {
|
|
| 63 |
+ return initFunc(path.Join(home, name)) |
|
| 64 |
+ } |
|
| 65 |
+ return nil, fmt.Errorf("No such driver: %s", name)
|
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+func New(root string) (driver Driver, err error) {
|
|
| 69 |
+ for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
|
|
| 70 |
+ if name != "" {
|
|
| 71 |
+ return GetDriver(name, root) |
|
| 72 |
+ } |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ // Check for priority drivers first |
|
| 76 |
+ for _, name := range priority {
|
|
| 77 |
+ if driver, err = GetDriver(name, root); err != nil {
|
|
| 78 |
+ utils.Debugf("Error loading driver %s: %s", name, err)
|
|
| 79 |
+ continue |
|
| 80 |
+ } |
|
| 81 |
+ return driver, nil |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ // Check all registered drivers if no priority driver is found |
|
| 85 |
+ for _, initFunc := range drivers {
|
|
| 86 |
+ if driver, err = initFunc(root); err != nil {
|
|
| 87 |
+ continue |
|
| 88 |
+ } |
|
| 89 |
+ return driver, nil |
|
| 90 |
+ } |
|
| 91 |
+ return nil, err |
|
| 92 |
+} |
| 0 | 93 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,95 @@ |
| 0 |
+package vfs |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 5 |
+ "os" |
|
| 6 |
+ "os/exec" |
|
| 7 |
+ "path" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func init() {
|
|
| 11 |
+ graphdriver.Register("vfs", Init)
|
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+func Init(home string) (graphdriver.Driver, error) {
|
|
| 15 |
+ d := &Driver{
|
|
| 16 |
+ home: home, |
|
| 17 |
+ } |
|
| 18 |
+ return d, nil |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+type Driver struct {
|
|
| 22 |
+ home string |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func (d *Driver) String() string {
|
|
| 26 |
+ return "vfs" |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+func (d *Driver) Status() [][2]string {
|
|
| 30 |
+ return nil |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+func (d *Driver) Cleanup() error {
|
|
| 34 |
+ return nil |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func copyDir(src, dst string) error {
|
|
| 38 |
+ if output, err := exec.Command("cp", "-aT", "--reflink=auto", src, dst).CombinedOutput(); err != nil {
|
|
| 39 |
+ return fmt.Errorf("Error VFS copying directory: %s (%s)", err, output)
|
|
| 40 |
+ } |
|
| 41 |
+ return nil |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func (d *Driver) Create(id string, parent string) error {
|
|
| 45 |
+ dir := d.dir(id) |
|
| 46 |
+ if err := os.MkdirAll(path.Dir(dir), 0700); err != nil {
|
|
| 47 |
+ return err |
|
| 48 |
+ } |
|
| 49 |
+ if err := os.Mkdir(dir, 0700); err != nil {
|
|
| 50 |
+ return err |
|
| 51 |
+ } |
|
| 52 |
+ if parent == "" {
|
|
| 53 |
+ return nil |
|
| 54 |
+ } |
|
| 55 |
+ parentDir, err := d.Get(parent) |
|
| 56 |
+ if err != nil {
|
|
| 57 |
+ return fmt.Errorf("%s: %s", parent, err)
|
|
| 58 |
+ } |
|
| 59 |
+ if err := copyDir(parentDir, dir); err != nil {
|
|
| 60 |
+ return err |
|
| 61 |
+ } |
|
| 62 |
+ return nil |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+func (d *Driver) dir(id string) string {
|
|
| 66 |
+ return path.Join(d.home, "dir", path.Base(id)) |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+func (d *Driver) Remove(id string) error {
|
|
| 70 |
+ if _, err := os.Stat(d.dir(id)); err != nil {
|
|
| 71 |
+ return err |
|
| 72 |
+ } |
|
| 73 |
+ return os.RemoveAll(d.dir(id)) |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func (d *Driver) Get(id string) (string, error) {
|
|
| 77 |
+ dir := d.dir(id) |
|
| 78 |
+ if st, err := os.Stat(dir); err != nil {
|
|
| 79 |
+ return "", err |
|
| 80 |
+ } else if !st.IsDir() {
|
|
| 81 |
+ return "", fmt.Errorf("%s: not a directory", dir)
|
|
| 82 |
+ } |
|
| 83 |
+ return dir, nil |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+func (d *Driver) Put(id string) {
|
|
| 87 |
+ // The vfs driver has no runtime resources (e.g. mounts) |
|
| 88 |
+ // to clean up, so we don't need anything here |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+func (d *Driver) Exists(id string) bool {
|
|
| 92 |
+ _, err := os.Stat(d.dir(id)) |
|
| 93 |
+ return err == nil |
|
| 94 |
+} |
| ... | ... |
@@ -11,11 +11,11 @@ import ( |
| 11 | 11 |
"github.com/dotcloud/docker/runtime/execdriver/execdrivers" |
| 12 | 12 |
"github.com/dotcloud/docker/runtime/execdriver/lxc" |
| 13 | 13 |
"github.com/dotcloud/docker/graph" |
| 14 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 15 |
- "github.com/dotcloud/docker/graphdriver/aufs" |
|
| 16 |
- _ "github.com/dotcloud/docker/graphdriver/btrfs" |
|
| 17 |
- _ "github.com/dotcloud/docker/graphdriver/devmapper" |
|
| 18 |
- _ "github.com/dotcloud/docker/graphdriver/vfs" |
|
| 14 |
+ "github.com/dotcloud/docker/runtime/graphdriver" |
|
| 15 |
+ "github.com/dotcloud/docker/runtime/graphdriver/aufs" |
|
| 16 |
+ _ "github.com/dotcloud/docker/runtime/graphdriver/btrfs" |
|
| 17 |
+ _ "github.com/dotcloud/docker/runtime/graphdriver/devmapper" |
|
| 18 |
+ _ "github.com/dotcloud/docker/runtime/graphdriver/vfs" |
|
| 19 | 19 |
"github.com/dotcloud/docker/image" |
| 20 | 20 |
_ "github.com/dotcloud/docker/runtime/networkdriver/lxc" |
| 21 | 21 |
"github.com/dotcloud/docker/runtime/networkdriver/portallocator" |