github.com/pmorton/docker@v1.5.0/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 // Useful helper for cleanup 343 func RemoveDevice(name string) error { 344 log.Debugf("[devmapper] RemoveDevice START(%s)", name) 345 defer log.Debugf("[devmapper] RemoveDevice END(%s)", name) 346 task, err := TaskCreateNamed(DeviceRemove, name) 347 if task == nil { 348 return err 349 } 350 351 var cookie uint = 0 352 if err := task.SetCookie(&cookie, 0); err != nil { 353 return fmt.Errorf("Can not set cookie: %s", err) 354 } 355 defer UdevWait(cookie) 356 357 dmSawBusy = false // reset before the task is run 358 if err = task.Run(); err != nil { 359 if dmSawBusy { 360 return ErrBusy 361 } 362 return fmt.Errorf("Error running RemoveDevice %s", err) 363 } 364 365 return nil 366 } 367 368 func GetBlockDeviceSize(file *os.File) (uint64, error) { 369 size, err := ioctlBlkGetSize64(file.Fd()) 370 if err != nil { 371 log.Errorf("Error getblockdevicesize: %s", err) 372 return 0, ErrGetBlockSize 373 } 374 return uint64(size), nil 375 } 376 377 func BlockDeviceDiscard(path string) error { 378 file, err := os.OpenFile(path, os.O_RDWR, 0) 379 if err != nil { 380 return err 381 } 382 defer file.Close() 383 384 size, err := GetBlockDeviceSize(file) 385 if err != nil { 386 return err 387 } 388 389 if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil { 390 return err 391 } 392 393 // Without this sometimes the remove of the device that happens after 394 // discard fails with EBUSY. 395 syscall.Sync() 396 397 return nil 398 } 399 400 // This is the programmatic example of "dmsetup create" 401 func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error { 402 task, err := TaskCreateNamed(DeviceCreate, poolName) 403 if task == nil { 404 return err 405 } 406 407 size, err := GetBlockDeviceSize(dataFile) 408 if err != nil { 409 return fmt.Errorf("Can't get data size %s", err) 410 } 411 412 params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize) 413 if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil { 414 return fmt.Errorf("Can't add target %s", err) 415 } 416 417 var cookie uint = 0 418 var flags uint16 = DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag 419 if err := task.SetCookie(&cookie, flags); err != nil { 420 return fmt.Errorf("Can't set cookie %s", err) 421 } 422 defer UdevWait(cookie) 423 424 if err := task.Run(); err != nil { 425 return fmt.Errorf("Error running DeviceCreate (CreatePool) %s", err) 426 } 427 428 return nil 429 } 430 431 func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error { 432 task, err := TaskCreateNamed(DeviceReload, poolName) 433 if task == nil { 434 return err 435 } 436 437 size, err := GetBlockDeviceSize(dataFile) 438 if err != nil { 439 return fmt.Errorf("Can't get data size %s", err) 440 } 441 442 params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize) 443 if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil { 444 return fmt.Errorf("Can't add target %s", err) 445 } 446 447 if err := task.Run(); err != nil { 448 return fmt.Errorf("Error running DeviceCreate %s", err) 449 } 450 451 return nil 452 } 453 454 func GetDeps(name string) (*Deps, error) { 455 task, err := TaskCreateNamed(DeviceDeps, name) 456 if task == nil { 457 return nil, err 458 } 459 if err := task.Run(); err != nil { 460 return nil, err 461 } 462 return task.GetDeps() 463 } 464 465 func GetInfo(name string) (*Info, error) { 466 task, err := TaskCreateNamed(DeviceInfo, name) 467 if task == nil { 468 return nil, err 469 } 470 if err := task.Run(); err != nil { 471 return nil, err 472 } 473 return task.GetInfo() 474 } 475 476 func GetDriverVersion() (string, error) { 477 task := TaskCreate(DeviceVersion) 478 if task == nil { 479 return "", fmt.Errorf("Can't create DeviceVersion task") 480 } 481 if err := task.Run(); err != nil { 482 return "", err 483 } 484 return task.GetDriverVersion() 485 } 486 487 func GetStatus(name string) (uint64, uint64, string, string, error) { 488 task, err := TaskCreateNamed(DeviceStatus, name) 489 if task == nil { 490 log.Debugf("GetStatus: Error TaskCreateNamed: %s", err) 491 return 0, 0, "", "", err 492 } 493 if err := task.Run(); err != nil { 494 log.Debugf("GetStatus: Error Run: %s", err) 495 return 0, 0, "", "", err 496 } 497 498 devinfo, err := task.GetInfo() 499 if err != nil { 500 log.Debugf("GetStatus: Error GetInfo: %s", err) 501 return 0, 0, "", "", err 502 } 503 if devinfo.Exists == 0 { 504 log.Debugf("GetStatus: Non existing device %s", name) 505 return 0, 0, "", "", fmt.Errorf("Non existing device %s", name) 506 } 507 508 _, start, length, targetType, params := task.GetNextTarget(0) 509 return start, length, targetType, params, nil 510 } 511 512 func SetTransactionId(poolName string, oldId uint64, newId uint64) error { 513 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 514 if task == nil { 515 return err 516 } 517 518 if err := task.SetSector(0); err != nil { 519 return fmt.Errorf("Can't set sector %s", err) 520 } 521 522 if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil { 523 return fmt.Errorf("Can't set message %s", err) 524 } 525 526 if err := task.Run(); err != nil { 527 return fmt.Errorf("Error running SetTransactionId %s", err) 528 } 529 return nil 530 } 531 532 func SuspendDevice(name string) error { 533 task, err := TaskCreateNamed(DeviceSuspend, name) 534 if task == nil { 535 return err 536 } 537 if err := task.Run(); err != nil { 538 return fmt.Errorf("Error running DeviceSuspend %s", err) 539 } 540 return nil 541 } 542 543 func ResumeDevice(name string) error { 544 task, err := TaskCreateNamed(DeviceResume, name) 545 if task == nil { 546 return err 547 } 548 549 var cookie uint = 0 550 if err := task.SetCookie(&cookie, 0); err != nil { 551 return fmt.Errorf("Can't set cookie %s", err) 552 } 553 defer UdevWait(cookie) 554 555 if err := task.Run(); err != nil { 556 return fmt.Errorf("Error running DeviceResume %s", err) 557 } 558 559 return nil 560 } 561 562 func CreateDevice(poolName string, deviceId int) error { 563 log.Debugf("[devmapper] CreateDevice(poolName=%v, deviceId=%v)", poolName, deviceId) 564 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 565 if task == nil { 566 return err 567 } 568 569 if err := task.SetSector(0); err != nil { 570 return fmt.Errorf("Can't set sector %s", err) 571 } 572 573 if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil { 574 return fmt.Errorf("Can't set message %s", err) 575 } 576 577 dmSawExist = false // reset before the task is run 578 if err := task.Run(); err != nil { 579 // Caller wants to know about ErrDeviceIdExists so that it can try with a different device id. 580 if dmSawExist { 581 return ErrDeviceIdExists 582 } else { 583 return fmt.Errorf("Error running CreateDevice %s", err) 584 } 585 } 586 return nil 587 } 588 589 func DeleteDevice(poolName string, deviceId int) error { 590 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 591 if task == nil { 592 return err 593 } 594 595 if err := task.SetSector(0); err != nil { 596 return fmt.Errorf("Can't set sector %s", err) 597 } 598 599 if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil { 600 return fmt.Errorf("Can't set message %s", err) 601 } 602 603 if err := task.Run(); err != nil { 604 return fmt.Errorf("Error running DeleteDevice %s", err) 605 } 606 return nil 607 } 608 609 func ActivateDevice(poolName string, name string, deviceId int, size uint64) error { 610 task, err := TaskCreateNamed(DeviceCreate, name) 611 if task == nil { 612 return err 613 } 614 615 params := fmt.Sprintf("%s %d", poolName, deviceId) 616 if err := task.AddTarget(0, size/512, "thin", params); err != nil { 617 return fmt.Errorf("Can't add target %s", err) 618 } 619 if err := task.SetAddNode(AddNodeOnCreate); err != nil { 620 return fmt.Errorf("Can't add node %s", err) 621 } 622 623 var cookie uint = 0 624 if err := task.SetCookie(&cookie, 0); err != nil { 625 return fmt.Errorf("Can't set cookie %s", err) 626 } 627 628 defer UdevWait(cookie) 629 630 if err := task.Run(); err != nil { 631 return fmt.Errorf("Error running DeviceCreate (ActivateDevice) %s", err) 632 } 633 634 return nil 635 } 636 637 func CreateSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error { 638 devinfo, _ := GetInfo(baseName) 639 doSuspend := devinfo != nil && devinfo.Exists != 0 640 641 if doSuspend { 642 if err := SuspendDevice(baseName); err != nil { 643 return err 644 } 645 } 646 647 task, err := TaskCreateNamed(DeviceTargetMsg, poolName) 648 if task == nil { 649 if doSuspend { 650 ResumeDevice(baseName) 651 } 652 return err 653 } 654 655 if err := task.SetSector(0); err != nil { 656 if doSuspend { 657 ResumeDevice(baseName) 658 } 659 return fmt.Errorf("Can't set sector %s", err) 660 } 661 662 if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil { 663 if doSuspend { 664 ResumeDevice(baseName) 665 } 666 return fmt.Errorf("Can't set message %s", err) 667 } 668 669 dmSawExist = false // reset before the task is run 670 if err := task.Run(); err != nil { 671 if doSuspend { 672 ResumeDevice(baseName) 673 } 674 // Caller wants to know about ErrDeviceIdExists so that it can try with a different device id. 675 if dmSawExist { 676 return ErrDeviceIdExists 677 } else { 678 return fmt.Errorf("Error running DeviceCreate (createSnapDevice) %s", err) 679 } 680 } 681 682 if doSuspend { 683 if err := ResumeDevice(baseName); err != nil { 684 return err 685 } 686 } 687 688 return nil 689 }