github.com/christopherobin/docker@v1.6.2/pkg/devicemapper/devmapper.go (about) 1 // +build linux 2 3 package devicemapper 4 5 import ( 6 "errors" 7 "fmt" 8 "os" 9 "runtime" 10 "syscall" 11 12 log "github.com/Sirupsen/logrus" 13 ) 14 15 type DevmapperLogger interface { 16 DMLog(level int, file string, line int, dmError int, message string) 17 } 18 19 const ( 20 DeviceCreate TaskType = iota 21 DeviceReload 22 DeviceRemove 23 DeviceRemoveAll 24 DeviceSuspend 25 DeviceResume 26 DeviceInfo 27 DeviceDeps 28 DeviceRename 29 DeviceVersion 30 DeviceStatus 31 DeviceTable 32 DeviceWaitevent 33 DeviceList 34 DeviceClear 35 DeviceMknodes 36 DeviceListVersions 37 DeviceTargetMsg 38 DeviceSetGeometry 39 ) 40 41 const ( 42 AddNodeOnResume AddNodeType = iota 43 AddNodeOnCreate 44 ) 45 46 var ( 47 ErrTaskRun = errors.New("dm_task_run failed") 48 ErrTaskSetName = errors.New("dm_task_set_name failed") 49 ErrTaskSetMessage = errors.New("dm_task_set_message failed") 50 ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed") 51 ErrTaskSetRo = errors.New("dm_task_set_ro failed") 52 ErrTaskAddTarget = errors.New("dm_task_add_target failed") 53 ErrTaskSetSector = errors.New("dm_task_set_sector failed") 54 ErrTaskGetDeps = errors.New("dm_task_get_deps failed") 55 ErrTaskGetInfo = errors.New("dm_task_get_info failed") 56 ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed") 57 ErrTaskSetCookie = errors.New("dm_task_set_cookie failed") 58 ErrNilCookie = errors.New("cookie ptr can't be nil") 59 ErrAttachLoopbackDevice = errors.New("loopback mounting failed") 60 ErrGetBlockSize = errors.New("Can't get block size") 61 ErrUdevWait = errors.New("wait on udev cookie failed") 62 ErrSetDevDir = errors.New("dm_set_dev_dir failed") 63 ErrGetLibraryVersion = errors.New("dm_get_library_version failed") 64 ErrCreateRemoveTask = errors.New("Can't create task of type DeviceRemove") 65 ErrRunRemoveDevice = errors.New("running RemoveDevice failed") 66 ErrInvalidAddNode = errors.New("Invalid AddNode type") 67 ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file") 68 ErrLoopbackSetCapacity = errors.New("Unable set loopback capacity") 69 ErrBusy = errors.New("Device is Busy") 70 ErrDeviceIdExists = errors.New("Device Id Exists") 71 72 dmSawBusy bool 73 dmSawExist bool 74 ) 75 76 type ( 77 Task struct { 78 unmanaged *CDmTask 79 } 80 Deps struct { 81 Count uint32 82 Filler uint32 83 Device []uint64 84 } 85 Info struct { 86 Exists int 87 Suspended int 88 LiveTable int 89 InactiveTable int 90 OpenCount int32 91 EventNr uint32 92 Major uint32 93 Minor uint32 94 ReadOnly int 95 TargetCount int32 96 } 97 TaskType int 98 AddNodeType int 99 ) 100 101 // Returns whether error conveys the information about device Id already 102 // exist or not. This will be true if device creation or snap creation 103 // operation fails if device or snap device already exists in pool. 104 // Current implementation is little crude as it scans the error string 105 // for exact pattern match. Replacing it with more robust implementation 106 // is desirable. 107 func DeviceIdExists(err error) bool { 108 return fmt.Sprint(err) == fmt.Sprint(ErrDeviceIdExists) 109 } 110 111 func (t *Task) destroy() { 112 if t != nil { 113 DmTaskDestroy(t.unmanaged) 114 runtime.SetFinalizer(t, nil) 115 } 116 } 117 118 // TaskCreateNamed is a convenience function for TaskCreate when a name 119 // will be set on the task as well 120 func TaskCreateNamed(t TaskType, name string) (*Task, error) { 121 task := TaskCreate(t) 122 if task == nil { 123 return nil, fmt.Errorf("Can't create task of type %d", int(t)) 124 } 125 if err := task.SetName(name); err != nil { 126 return nil, fmt.Errorf("Can't set task name %s", name) 127 } 128 return task, nil 129 } 130 131 // TaskCreate initializes a devicemapper task of tasktype 132 func TaskCreate(tasktype TaskType) *Task { 133 Ctask := DmTaskCreate(int(tasktype)) 134 if Ctask == nil { 135 return nil 136 } 137 task := &Task{unmanaged: Ctask} 138 runtime.SetFinalizer(task, (*Task).destroy) 139 return task 140 } 141 142 func (t *Task) Run() error { 143 if res := DmTaskRun(t.unmanaged); res != 1 { 144 return ErrTaskRun 145 } 146 return nil 147 } 148 149 func (t *Task) SetName(name string) error { 150 if res := DmTaskSetName(t.unmanaged, name); res != 1 { 151 return ErrTaskSetName 152 } 153 return nil 154 } 155 156 func (t *Task) SetMessage(message string) error { 157 if res := DmTaskSetMessage(t.unmanaged, message); res != 1 { 158 return ErrTaskSetMessage 159 } 160 return nil 161 } 162 163 func (t *Task) SetSector(sector uint64) error { 164 if res := DmTaskSetSector(t.unmanaged, sector); res != 1 { 165 return ErrTaskSetSector 166 } 167 return nil 168 } 169 170 func (t *Task) SetCookie(cookie *uint, flags uint16) error { 171 if cookie == nil { 172 return ErrNilCookie 173 } 174 if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 { 175 return ErrTaskSetCookie 176 } 177 return nil 178 } 179 180 func (t *Task) SetAddNode(addNode AddNodeType) error { 181 if addNode != AddNodeOnResume && addNode != AddNodeOnCreate { 182 return ErrInvalidAddNode 183 } 184 if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 { 185 return ErrTaskSetAddNode 186 } 187 return nil 188 } 189 190 func (t *Task) SetRo() error { 191 if res := DmTaskSetRo(t.unmanaged); res != 1 { 192 return ErrTaskSetRo 193 } 194 return nil 195 } 196 197 func (t *Task) AddTarget(start, size uint64, ttype, params string) error { 198 if res := DmTaskAddTarget(t.unmanaged, start, size, 199 ttype, params); res != 1 { 200 return ErrTaskAddTarget 201 } 202 return nil 203 } 204 205 func (t *Task) GetDeps() (*Deps, error) { 206 var deps *Deps 207 if deps = DmTaskGetDeps(t.unmanaged); deps == nil { 208 return nil, ErrTaskGetDeps 209 } 210 return deps, nil 211 } 212 213 func (t *Task) GetInfo() (*Info, error) { 214 info := &Info{} 215 if res := DmTaskGetInfo(t.unmanaged, info); res != 1 { 216 return nil, ErrTaskGetInfo 217 } 218 return info, nil 219 } 220 221 func (t *Task) GetDriverVersion() (string, error) { 222 res := DmTaskGetDriverVersion(t.unmanaged) 223 if res == "" { 224 return "", ErrTaskGetDriverVersion 225 } 226 return res, nil 227 } 228 229 func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, 230 length uint64, targetType string, params string) { 231 232 return DmGetNextTarget(t.unmanaged, next, &start, &length, 233 &targetType, ¶ms), 234 start, length, targetType, params 235 } 236 237 func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) { 238 loopInfo, err := ioctlLoopGetStatus64(file.Fd()) 239 if err != nil { 240 log.Errorf("Error get loopback backing file: %s", err) 241 return 0, 0, ErrGetLoopbackBackingFile 242 } 243 return loopInfo.loDevice, loopInfo.loInode, nil 244 } 245 246 func LoopbackSetCapacity(file *os.File) error { 247 if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil { 248 log.Errorf("Error loopbackSetCapacity: %s", err) 249 return ErrLoopbackSetCapacity 250 } 251 return nil 252 } 253 254 func FindLoopDeviceFor(file *os.File) *os.File { 255 stat, err := file.Stat() 256 if err != nil { 257 return nil 258 } 259 targetInode := stat.Sys().(*syscall.Stat_t).Ino 260 targetDevice := stat.Sys().(*syscall.Stat_t).Dev 261 262 for i := 0; true; i++ { 263 path := fmt.Sprintf("/dev/loop%d", i) 264 265 file, err := os.OpenFile(path, os.O_RDWR, 0) 266 if err != nil { 267 if os.IsNotExist(err) { 268 return nil 269 } 270 271 // Ignore all errors until the first not-exist 272 // we want to continue looking for the file 273 continue 274 } 275 276 dev, inode, err := getLoopbackBackingFile(file) 277 if err == nil && dev == targetDevice && inode == targetInode { 278 return file 279 } 280 file.Close() 281 } 282 283 return nil 284 } 285 286 func UdevWait(cookie uint) error { 287 if res := DmUdevWait(cookie); res != 1 { 288 log.Debugf("Failed to wait on udev cookie %d", cookie) 289 return ErrUdevWait 290 } 291 return nil 292 } 293 294 func LogInitVerbose(level int) { 295 DmLogInitVerbose(level) 296 } 297 298 var dmLogger DevmapperLogger = nil 299 300 // initialize the logger for the device mapper library 301 func LogInit(logger DevmapperLogger) { 302 dmLogger = logger 303 LogWithErrnoInit() 304 } 305 306 func SetDevDir(dir string) error { 307 if res := DmSetDevDir(dir); res != 1 { 308 log.Debugf("Error dm_set_dev_dir") 309 return ErrSetDevDir 310 } 311 return nil 312 } 313 314 func GetLibraryVersion() (string, error) { 315 var version string 316 if res := DmGetLibraryVersion(&version); res != 1 { 317 return "", ErrGetLibraryVersion 318 } 319 return version, nil 320 } 321 322 // UdevSyncSupported returns whether device-mapper is able to sync with udev 323 // 324 // This is essential otherwise race conditions can arise where both udev and 325 // device-mapper attempt to create and destroy devices. 326 func UdevSyncSupported() bool { 327 return DmUdevGetSyncSupport() != 0 328 } 329 330 // UdevSetSyncSupport allows setting whether the udev sync should be enabled. 331 // The return bool indicates the state of whether the sync is enabled. 332 func UdevSetSyncSupport(enable bool) bool { 333 if enable { 334 DmUdevSetSyncSupport(1) 335 } else { 336 DmUdevSetSyncSupport(0) 337 } 338 339 return UdevSyncSupported() 340 } 341 342 // CookieSupported returns whether the version of device-mapper supports the 343 // use of cookie's in the tasks. 344 // This is largely a lower level call that other functions use. 345 func CookieSupported() bool { 346 return DmCookieSupported() != 0 347 } 348 349 // Useful helper for cleanup 350 func RemoveDevice(name string) error { 351 log.Debugf("[devmapper] RemoveDevice START(%s)", name) 352 defer log.Debugf("[devmapper] RemoveDevice END(%s)", name) 353 task, err := TaskCreateNamed(DeviceRemove, name) 354 if task == nil { 355 return err 356 } 357 358 var cookie uint = 0 359 if err := task.SetCookie(&cookie, 0); err != nil { 360 return fmt.Errorf("Can not set cookie: %s", err) 361 } 362 defer UdevWait(cookie) 363 364 dmSawBusy = false // reset before the task is run 365 if err = task.Run(); err != nil { 366 if dmSawBusy { 367 return ErrBusy 368 } 369 return fmt.Errorf("Error running RemoveDevice %s", err) 370 } 371 372 return nil 373 } 374 375 func GetBlockDeviceSize(file *os.File) (uint64, error) { 376 size, err := ioctlBlkGetSize64(file.Fd()) 377 if err != nil { 378 log.Errorf("Error getblockdevicesize: %s", err) 379 return 0, ErrGetBlockSize 380 } 381 return uint64(size), nil 382 } 383 384 func BlockDeviceDiscard(path string) error { 385 file, err := os.OpenFile(path, os.O_RDWR, 0) 386 if err != nil { 387 return err 388 } 389 defer file.Close() 390 391 size, err := GetBlockDeviceSize(file) 392 if err != nil { 393 return err 394 } 395 396 if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil { 397 return err 398 } 399 400 // Without this sometimes the remove of the device that happens after 401 // discard fails with EBUSY. 402 syscall.Sync() 403 404 return nil 405 } 406 407 // This is the programmatic example of "dmsetup create" 408 func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error { 409 task, err := TaskCreateNamed(DeviceCreate, poolName) 410 if task == nil { 411 return err 412 } 413 414 size, err := GetBlockDeviceSize(dataFile) 415 if err != nil { 416 return fmt.Errorf("Can't get data size %s", err) 417 } 418 419 params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize) 420 if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil { 421 return fmt.Errorf("Can't add target %s", err) 422 } 423 424 var cookie uint = 0 425 var flags uint16 = DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag 426 if err := task.SetCookie(&cookie, flags); err != nil { 427 return fmt.Errorf("Can't set cookie %s", err) 428 } 429 defer UdevWait(cookie) 430 431 if err := task.Run(); err != nil { 432 return fmt.Errorf("Error running DeviceCreate (CreatePool) %s", err) 433 } 434 435 return nil 436 } 437 438 func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error { 439 task, err := TaskCreateNamed(DeviceReload, poolName) 440 if task == nil { 441 return err 442 } 443 444 size, err := GetBlockDeviceSize(dataFile) 445 if err != nil { 446 return fmt.Errorf("Can't get data size %s", err) 447 } 448 449 params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize) 450 if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil { 451 return fmt.Errorf("Can't add target %s", err) 452 } 453 454 if err := task.Run(); err != nil { 455 return fmt.Errorf("Error running DeviceCreate %s", err) 456 } 457 458 return nil 459 } 460 461 func GetDeps(name string) (*Deps, error) { 462 task, err := TaskCreateNamed(DeviceDeps, name) 463 if task == nil { 464 return nil, err 465 } 466 if err := task.Run(); err != nil { 467 return nil, err 468 } 469 return task.GetDeps() 470 } 471 472 func GetInfo(name string) (*Info, error) { 473 task, err := TaskCreateNamed(DeviceInfo, name) 474 if task == nil { 475 return nil, err 476 } 477 if err := task.Run(); err != nil { 478 return nil, err 479 } 480 return task.GetInfo() 481 } 482 483 func GetDriverVersion() (string, error) { 484 task := TaskCreate(DeviceVersion) 485 if task == nil { 486 return "", fmt.Errorf("Can't create DeviceVersion task") 487 } 488 if err := task.Run(); err != nil { 489 return "", err 490 } 491 return task.GetDriverVersion() 492 } 493 494 func GetStatus(name string) (uint64, uint64, string, string, error) { 495 task, err := TaskCreateNamed(DeviceStatus, name) 496 if task == nil { 497 log.Debugf("GetStatus: Error TaskCreateNamed: %s", err) 498 return 0, 0, "", "", err 499 } 500 if err := task.Run(); err != nil { 501 log.Debugf("GetStatus: Error Run: %s", err) 502 return 0, 0, "", "", err 503 } 504 505 devinfo, err := task.GetInfo() 506 if err != nil { 507 log.Debugf("GetStatus: Error GetInfo: %s", err) 508 return 0, 0, "", "", err 509 } 510 if devinfo.Exists == 0 { 511 log.Debugf("GetStatus: Non existing device %s", name) 512 return 0, 0, "", "", fmt.Errorf("Non existing device %s", name) 513 } 514 515 _, start, length, targetType, params := task.GetNextTarget(0) 516 return start, length, targetType, params, nil 517 } 518 519 func SetTransactionId(poolName string, oldId uint64, newId uint64) error { 520 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 521 if task == nil { 522 return err 523 } 524 525 if err := task.SetSector(0); err != nil { 526 return fmt.Errorf("Can't set sector %s", err) 527 } 528 529 if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil { 530 return fmt.Errorf("Can't set message %s", err) 531 } 532 533 if err := task.Run(); err != nil { 534 return fmt.Errorf("Error running SetTransactionId %s", err) 535 } 536 return nil 537 } 538 539 func SuspendDevice(name string) error { 540 task, err := TaskCreateNamed(DeviceSuspend, name) 541 if task == nil { 542 return err 543 } 544 if err := task.Run(); err != nil { 545 return fmt.Errorf("Error running DeviceSuspend %s", err) 546 } 547 return nil 548 } 549 550 func ResumeDevice(name string) error { 551 task, err := TaskCreateNamed(DeviceResume, name) 552 if task == nil { 553 return err 554 } 555 556 var cookie uint = 0 557 if err := task.SetCookie(&cookie, 0); err != nil { 558 return fmt.Errorf("Can't set cookie %s", err) 559 } 560 defer UdevWait(cookie) 561 562 if err := task.Run(); err != nil { 563 return fmt.Errorf("Error running DeviceResume %s", err) 564 } 565 566 return nil 567 } 568 569 func CreateDevice(poolName string, deviceId int) error { 570 log.Debugf("[devmapper] CreateDevice(poolName=%v, deviceId=%v)", poolName, deviceId) 571 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 572 if task == nil { 573 return err 574 } 575 576 if err := task.SetSector(0); err != nil { 577 return fmt.Errorf("Can't set sector %s", err) 578 } 579 580 if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil { 581 return fmt.Errorf("Can't set message %s", err) 582 } 583 584 dmSawExist = false // reset before the task is run 585 if err := task.Run(); err != nil { 586 // Caller wants to know about ErrDeviceIdExists so that it can try with a different device id. 587 if dmSawExist { 588 return ErrDeviceIdExists 589 } else { 590 return fmt.Errorf("Error running CreateDevice %s", err) 591 } 592 } 593 return nil 594 } 595 596 func DeleteDevice(poolName string, deviceId int) error { 597 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 598 if task == nil { 599 return err 600 } 601 602 if err := task.SetSector(0); err != nil { 603 return fmt.Errorf("Can't set sector %s", err) 604 } 605 606 if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil { 607 return fmt.Errorf("Can't set message %s", err) 608 } 609 610 if err := task.Run(); err != nil { 611 return fmt.Errorf("Error running DeleteDevice %s", err) 612 } 613 return nil 614 } 615 616 func ActivateDevice(poolName string, name string, deviceId int, size uint64) error { 617 task, err := TaskCreateNamed(DeviceCreate, name) 618 if task == nil { 619 return err 620 } 621 622 params := fmt.Sprintf("%s %d", poolName, deviceId) 623 if err := task.AddTarget(0, size/512, "thin", params); err != nil { 624 return fmt.Errorf("Can't add target %s", err) 625 } 626 if err := task.SetAddNode(AddNodeOnCreate); err != nil { 627 return fmt.Errorf("Can't add node %s", err) 628 } 629 630 var cookie uint = 0 631 if err := task.SetCookie(&cookie, 0); err != nil { 632 return fmt.Errorf("Can't set cookie %s", err) 633 } 634 635 defer UdevWait(cookie) 636 637 if err := task.Run(); err != nil { 638 return fmt.Errorf("Error running DeviceCreate (ActivateDevice) %s", err) 639 } 640 641 return nil 642 } 643 644 func CreateSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error { 645 devinfo, _ := GetInfo(baseName) 646 doSuspend := devinfo != nil && devinfo.Exists != 0 647 648 if doSuspend { 649 if err := SuspendDevice(baseName); err != nil { 650 return err 651 } 652 } 653 654 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 655 if task == nil { 656 if doSuspend { 657 ResumeDevice(baseName) 658 } 659 return err 660 } 661 662 if err := task.SetSector(0); err != nil { 663 if doSuspend { 664 ResumeDevice(baseName) 665 } 666 return fmt.Errorf("Can't set sector %s", err) 667 } 668 669 if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil { 670 if doSuspend { 671 ResumeDevice(baseName) 672 } 673 return fmt.Errorf("Can't set message %s", err) 674 } 675 676 dmSawExist = false // reset before the task is run 677 if err := task.Run(); err != nil { 678 if doSuspend { 679 ResumeDevice(baseName) 680 } 681 // Caller wants to know about ErrDeviceIdExists so that it can try with a different device id. 682 if dmSawExist { 683 return ErrDeviceIdExists 684 } else { 685 return fmt.Errorf("Error running DeviceCreate (createSnapDevice) %s", err) 686 } 687 } 688 689 if doSuspend { 690 if err := ResumeDevice(baseName); err != nil { 691 return err 692 } 693 } 694 695 return nil 696 }