github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/cmd/installer/configureStorage.go (about) 1 // +build linux 2 3 package main 4 5 import ( 6 "bufio" 7 "bytes" 8 "crypto/rand" 9 "encoding/gob" 10 "errors" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "os" 15 "path/filepath" 16 "sort" 17 "strconv" 18 "strings" 19 "sync" 20 "syscall" 21 "time" 22 23 imageclient "github.com/Cloud-Foundations/Dominator/imageserver/client" 24 "github.com/Cloud-Foundations/Dominator/lib/concurrent" 25 "github.com/Cloud-Foundations/Dominator/lib/cpusharer" 26 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 27 "github.com/Cloud-Foundations/Dominator/lib/filesystem/util" 28 "github.com/Cloud-Foundations/Dominator/lib/format" 29 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 30 "github.com/Cloud-Foundations/Dominator/lib/image" 31 "github.com/Cloud-Foundations/Dominator/lib/json" 32 "github.com/Cloud-Foundations/Dominator/lib/log" 33 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 34 objectclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 35 "github.com/Cloud-Foundations/Dominator/lib/srpc" 36 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 37 fm_proto "github.com/Cloud-Foundations/Dominator/proto/fleetmanager" 38 installer_proto "github.com/Cloud-Foundations/Dominator/proto/installer" 39 ) 40 41 const ( 42 keyFile = "/etc/crypt.key" 43 ) 44 45 type driveType struct { 46 discarded bool 47 devpath string 48 name string 49 size uint64 // Bytes 50 } 51 52 type kexecRebooter struct { 53 util.BootInfoType 54 logger log.DebugLogger 55 } 56 57 func init() { 58 gob.Register(&filesystem.RegularInode{}) 59 gob.Register(&filesystem.SymlinkInode{}) 60 gob.Register(&filesystem.SpecialInode{}) 61 gob.Register(&filesystem.DirectoryInode{}) 62 } 63 64 func closeEncryptedVolumes(logger log.DebugLogger) error { 65 if file, err := os.Open("/dev/mapper"); err != nil { 66 return err 67 } else { 68 defer file.Close() 69 if names, err := file.Readdirnames(-1); err != nil { 70 return err 71 } else { 72 for _, name := range names { 73 if name == "control" { 74 continue 75 } 76 err := run("cryptsetup", *tmpRoot, logger, "close", name) 77 if err != nil { 78 return err 79 } 80 } 81 return nil 82 } 83 } 84 } 85 86 func configureBootDrive(cpuSharer cpusharer.CpuSharer, drive *driveType, 87 layout installer_proto.StorageLayout, bootPartition int, img *image.Image, 88 objGetter objectserver.ObjectsGetter, logger log.DebugLogger) error { 89 startTime := time.Now() 90 if run("blkdiscard", *tmpRoot, logger, drive.devpath) == nil { 91 drive.discarded = true 92 logger.Printf("discarded %s in %s\n", 93 drive.devpath, format.Duration(time.Since(startTime))) 94 } else { // Erase old partition. 95 if err := eraseStart(drive.devpath, logger); err != nil { 96 return err 97 } 98 } 99 args := []string{"-s", "-a", "cylinder", drive.devpath, "mklabel", "msdos"} 100 unitSize := uint64(1 << 20) 101 unitSuffix := "MiB" 102 offsetInUnits := uint64(1) 103 for _, partition := range layout.BootDriveLayout { 104 sizeInUnits := partition.MinimumFreeBytes / unitSize 105 if sizeInUnits*unitSize < partition.MinimumFreeBytes { 106 sizeInUnits++ 107 } 108 args = append(args, "mkpart", "primary", "ext2", 109 strconv.FormatUint(offsetInUnits, 10)+unitSuffix, 110 strconv.FormatUint(offsetInUnits+sizeInUnits, 10)+unitSuffix) 111 offsetInUnits += sizeInUnits 112 } 113 args = append(args, "mkpart", "primary", "ext2", 114 strconv.FormatUint(offsetInUnits, 10)+unitSuffix, "100%") 115 args = append(args, 116 "set", strconv.FormatInt(int64(bootPartition), 10), "boot", "on") 117 if err := run("parted", *tmpRoot, logger, args...); err != nil { 118 return err 119 } 120 // Prepare all file-systems concurrently, make them serially. 121 concurrentState := concurrent.NewState(uint( 122 len(layout.BootDriveLayout) + 1)) 123 var mkfsMutex sync.Mutex 124 for index, partition := range layout.BootDriveLayout { 125 device := partitionName(drive.devpath, index+1) 126 partition := partition 127 err := concurrentState.GoRun(func() error { 128 return drive.makeFileSystem(cpuSharer, device, partition.MountPoint, 129 "ext4", &mkfsMutex, 0, logger) 130 }) 131 if err != nil { 132 return err 133 } 134 } 135 concurrentState.GoRun(func() error { 136 device := partitionName(drive.devpath, len(layout.BootDriveLayout)+1) 137 return drive.makeFileSystem(cpuSharer, device, 138 layout.ExtraMountPointsBasename+"0", "ext4", &mkfsMutex, 65536, 139 logger) 140 }) 141 if err := concurrentState.Reap(); err != nil { 142 return err 143 } 144 // Mount all file-systems, except the data file-system. 145 for index, partition := range layout.BootDriveLayout { 146 device := partitionName(drive.devpath, index+1) 147 err := mount(remapDevice(device, partition.MountPoint), 148 filepath.Join(*mountPoint, partition.MountPoint), "ext4", logger) 149 if err != nil { 150 return err 151 } 152 } 153 return installRoot(drive.devpath, img.FileSystem, objGetter, logger) 154 } 155 156 func configureDataDrive(cpuSharer cpusharer.CpuSharer, drive *driveType, 157 index int, layout installer_proto.StorageLayout, 158 logger log.DebugLogger) error { 159 startTime := time.Now() 160 if run("blkdiscard", *tmpRoot, logger, drive.devpath) == nil { 161 drive.discarded = true 162 logger.Printf("discarded %s in %s\n", 163 drive.devpath, format.Duration(time.Since(startTime))) 164 } 165 dataMountPoint := layout.ExtraMountPointsBasename + strconv.FormatInt( 166 int64(index), 10) 167 return drive.makeFileSystem(cpuSharer, drive.devpath, dataMountPoint, 168 "ext4", nil, 1048576, logger) 169 } 170 171 func configureStorage(config fm_proto.GetMachineInfoResponse, 172 logger log.DebugLogger) (Rebooter, error) { 173 startTime := time.Now() 174 var layout installer_proto.StorageLayout 175 err := json.ReadFromFile(filepath.Join(*tftpDirectory, 176 "storage-layout.json"), 177 &layout) 178 if err != nil { 179 return nil, err 180 } 181 var bootPartition, rootPartition int 182 for index, partition := range layout.BootDriveLayout { 183 switch partition.MountPoint { 184 case "/": 185 rootPartition = index + 1 186 case "/boot": 187 bootPartition = index + 1 188 } 189 } 190 if rootPartition < 1 { 191 return nil, fmt.Errorf("no root partition specified in layout") 192 } 193 if bootPartition < 1 { 194 bootPartition = rootPartition 195 } 196 drives, err := listDrives(logger) 197 if err != nil { 198 return nil, err 199 } 200 rootDevice := partitionName(drives[0].devpath, rootPartition) 201 randomKey, err := getRandomKey(16, logger) 202 if err != nil { 203 return nil, err 204 } 205 imageName, img, client, err := getImage(logger) 206 if err != nil { 207 return nil, err 208 } 209 defer client.Close() 210 if img == nil { 211 logger.Println("no image specified, skipping paritioning") 212 return nil, nil 213 } else { 214 if err := img.FileSystem.RebuildInodePointers(); err != nil { 215 return nil, err 216 } 217 imageSize := img.FileSystem.EstimateUsage(0) 218 if layout.BootDriveLayout[rootPartition-1].MinimumFreeBytes < 219 imageSize { 220 layout.BootDriveLayout[rootPartition-1].MinimumFreeBytes = imageSize 221 } 222 layout.BootDriveLayout[rootPartition-1].MinimumFreeBytes += imageSize 223 } 224 var rebooter Rebooter 225 if layout.UseKexec { 226 bootInfo, err := util.GetBootInfo(img.FileSystem, "rootfs", "") 227 if err != nil { 228 return nil, err 229 } 230 rebooter = kexecRebooter{ 231 BootInfoType: *bootInfo, 232 logger: logger, 233 } 234 } 235 objClient := objectclient.AttachObjectClient(client) 236 defer objClient.Close() 237 objGetter, err := createObjectsCache(img.FileSystem.GetObjects(), objClient, 238 rootDevice, logger) 239 if err != nil { 240 return nil, err 241 } 242 if err := installTmpRoot(img.FileSystem, objGetter, logger); err != nil { 243 return nil, err 244 } 245 err = ioutil.WriteFile(filepath.Join(*tmpRoot, keyFile), randomKey, 246 fsutil.PrivateFilePerms) 247 if err != nil { 248 return nil, err 249 } 250 for index := range randomKey { // Scrub key. 251 randomKey[index] = 0 252 } 253 // Configure all drives concurrently, making file-systems. 254 // Use concurrent package because of it's reaping cabability. 255 // Use cpusharer package to limit CPU intensive operations. 256 concurrentState := concurrent.NewState(uint(len(drives))) 257 cpuSharer := cpusharer.NewFifoCpuSharer() 258 err = concurrentState.GoRun(func() error { 259 return configureBootDrive(cpuSharer, drives[0], layout, bootPartition, 260 img, objGetter, logger) 261 }) 262 if err != nil { 263 return nil, concurrentState.Reap() 264 } 265 for index, drive := range drives[1:] { 266 drive := drive 267 index := index + 1 268 err := concurrentState.GoRun(func() error { 269 return configureDataDrive(cpuSharer, drive, index, layout, logger) 270 }) 271 if err != nil { 272 break 273 } 274 } 275 if err := concurrentState.Reap(); err != nil { 276 return nil, err 277 } 278 // Make table entries for the boot device file-systems, except data FS. 279 fsTab := &bytes.Buffer{} 280 cryptTab := &bytes.Buffer{} 281 for index, partition := range layout.BootDriveLayout { 282 device := partitionName(drives[0].devpath, index+1) 283 err = drives[0].writeDeviceEntries(device, partition.MountPoint, "ext4", 284 fsTab, cryptTab, uint(index+1)) 285 if err != nil { 286 return nil, err 287 } 288 } 289 // Make table entries for data file-systems. 290 for index, drive := range drives { 291 checkCount := uint(2) 292 var device string 293 if index == 0 { // The boot device is partitioned. 294 checkCount = uint(len(layout.BootDriveLayout) + 1) 295 device = partitionName(drives[0].devpath, 296 len(layout.BootDriveLayout)+1) 297 } else { // Extra drives are used whole. 298 device = drive.devpath 299 } 300 dataMountPoint := layout.ExtraMountPointsBasename + strconv.FormatInt( 301 int64(index), 10) 302 err = drive.writeDeviceEntries(device, dataMountPoint, "ext4", fsTab, 303 cryptTab, checkCount) 304 if err != nil { 305 return nil, err 306 } 307 } 308 err = ioutil.WriteFile(filepath.Join(*mountPoint, "etc", "fstab"), 309 fsTab.Bytes(), fsutil.PublicFilePerms) 310 if err != nil { 311 return nil, err 312 } 313 err = ioutil.WriteFile(filepath.Join(*mountPoint, "/etc", "crypttab"), 314 cryptTab.Bytes(), fsutil.PublicFilePerms) 315 if err != nil { 316 return nil, err 317 } 318 // Copy key file and scrub temporary copy. 319 tmpKeyFile := filepath.Join(*tmpRoot, keyFile) 320 err = fsutil.CopyFile(filepath.Join(*mountPoint, keyFile), 321 tmpKeyFile, fsutil.PrivateFilePerms) 322 if err != nil { 323 return nil, err 324 } 325 if file, err := os.OpenFile(tmpKeyFile, os.O_WRONLY, 0); err != nil { 326 return nil, err 327 } else { 328 defer file.Close() 329 if _, err := file.Write(randomKey); err != nil { 330 return nil, err 331 } 332 } 333 logdir := filepath.Join(*mountPoint, "var", "log", "installer") 334 if err := os.MkdirAll(logdir, fsutil.DirPerms); err != nil { 335 return nil, err 336 } 337 if err := fsutil.CopyTree(logdir, *tftpDirectory); err != nil { 338 return nil, err 339 } 340 if err := util.WriteImageName(*mountPoint, imageName); err != nil { 341 return nil, err 342 } 343 logger.Printf("configureStorage() took %s\n", 344 format.Duration(time.Since(startTime))) 345 return rebooter, nil 346 } 347 348 func eraseStart(device string, logger log.DebugLogger) error { 349 if *dryRun { 350 logger.Debugf(0, "dry run: skipping erasure of: %s\n", device) 351 return nil 352 } 353 logger.Debugf(0, "erasing start of: %s\n", device) 354 file, err := os.OpenFile(device, os.O_WRONLY, 0) 355 if err != nil { 356 return err 357 } 358 defer file.Close() 359 var buffer [65536]byte 360 if _, err := file.Write(buffer[:]); err != nil { 361 return err 362 } 363 return nil 364 } 365 366 func getImage(logger log.DebugLogger) ( 367 string, *image.Image, *srpc.Client, error) { 368 data, err := ioutil.ReadFile(filepath.Join(*tftpDirectory, "imagename")) 369 if err != nil { 370 if os.IsNotExist(err) { 371 return "", nil, nil, nil 372 } 373 return "", nil, nil, err 374 } 375 imageName := strings.TrimSpace(string(data)) 376 data, err = ioutil.ReadFile(filepath.Join(*tftpDirectory, "imageserver")) 377 if err != nil { 378 return "", nil, nil, err 379 } 380 imageServerAddress := strings.TrimSpace(string(data)) 381 logger.Printf("dialing imageserver: %s\n", imageServerAddress) 382 startTime := time.Now() 383 client, err := srpc.DialHTTP("tcp", imageServerAddress, time.Second*15) 384 if err != nil { 385 return "", nil, nil, err 386 } 387 logger.Printf("dialed imageserver after: %s\n", 388 format.Duration(time.Since(startTime))) 389 startTime = time.Now() 390 if img, _ := imageclient.GetImage(client, imageName); img != nil { 391 logger.Debugf(0, "got image: %s in %s\n", 392 imageName, format.Duration(time.Since(startTime))) 393 return imageName, img, client, nil 394 } 395 streamName := imageName 396 isDir, err := imageclient.CheckDirectory(client, streamName) 397 if err != nil { 398 client.Close() 399 return "", nil, nil, err 400 } 401 if !isDir { 402 streamName = filepath.Dir(streamName) 403 isDir, err = imageclient.CheckDirectory(client, streamName) 404 if err != nil { 405 client.Close() 406 return "", nil, nil, err 407 } 408 } 409 if !isDir { 410 client.Close() 411 return "", nil, nil, fmt.Errorf("%s is not a directory", streamName) 412 } 413 imageName, err = imageclient.FindLatestImage(client, streamName, false) 414 if err != nil { 415 client.Close() 416 return "", nil, nil, err 417 } 418 if imageName == "" { 419 client.Close() 420 return "", nil, nil, fmt.Errorf("no image found in: %s on: %s", 421 streamName, imageServerAddress) 422 } 423 startTime = time.Now() 424 if img, err := imageclient.GetImage(client, imageName); err != nil { 425 client.Close() 426 return "", nil, nil, err 427 } else { 428 logger.Debugf(0, "got image: %s in %s\n", 429 imageName, format.Duration(time.Since(startTime))) 430 return imageName, img, client, nil 431 } 432 } 433 434 func getRandomKey(numBytes uint, logger log.DebugLogger) ([]byte, error) { 435 logger.Printf("getting %d random bytes\n", numBytes) 436 timer := time.AfterFunc(time.Second, func() { 437 logger.Println("getting random data is taking too long") 438 logger.Println("mash on the keyboard to add entropy") 439 }) 440 startTime := time.Now() 441 buffer := make([]byte, numBytes) 442 _, err := rand.Read(buffer) 443 timer.Stop() 444 if err != nil { 445 return nil, err 446 } else { 447 logger.Printf("read %d bytes of random data after %s\n", 448 numBytes, format.Duration(time.Since(startTime))) 449 return buffer, nil 450 } 451 } 452 453 func installRoot(device string, fileSystem *filesystem.FileSystem, 454 objGetter objectserver.ObjectsGetter, logger log.DebugLogger) error { 455 if *dryRun { 456 logger.Debugln(0, "dry run: skipping installing root") 457 return nil 458 } 459 logger.Debugln(0, "unpacking root") 460 err := util.Unpack(fileSystem, objGetter, *mountPoint, logger) 461 if err != nil { 462 return err 463 } 464 err = wsyscall.Mount("/dev", filepath.Join(*mountPoint, "dev"), "", 465 wsyscall.MS_BIND, "") 466 if err != nil { 467 return err 468 } 469 err = wsyscall.Mount("/proc", filepath.Join(*mountPoint, "proc"), "", 470 wsyscall.MS_BIND, "") 471 if err != nil { 472 return err 473 } 474 err = wsyscall.Mount("/sys", filepath.Join(*mountPoint, "sys"), "", 475 wsyscall.MS_BIND, "") 476 if err != nil { 477 return err 478 } 479 return util.MakeBootable(fileSystem, device, "rootfs", *mountPoint, "", 480 true, logger) 481 } 482 483 func installTmpRoot(fileSystem *filesystem.FileSystem, 484 objGetter objectserver.ObjectsGetter, logger log.DebugLogger) error { 485 if fi, err := os.Stat(*tmpRoot); err == nil { 486 if fi.IsDir() { 487 logger.Debugln(0, "tmproot already exists, not installing") 488 return nil 489 } 490 } 491 if *dryRun { 492 logger.Debugln(0, "dry run: skipping unpacking tmproot") 493 return nil 494 } 495 logger.Debugln(0, "unpacking tmproot") 496 if err := os.MkdirAll(*tmpRoot, fsutil.DirPerms); err != nil { 497 return err 498 } 499 syscall.Unmount(filepath.Join(*tmpRoot, "sys"), 0) 500 syscall.Unmount(filepath.Join(*tmpRoot, "proc"), 0) 501 syscall.Unmount(filepath.Join(*tmpRoot, "dev"), 0) 502 syscall.Unmount(*tmpRoot, 0) 503 if err := wsyscall.Mount("none", *tmpRoot, "tmpfs", 0, ""); err != nil { 504 return err 505 } 506 if err := util.Unpack(fileSystem, objGetter, *tmpRoot, logger); err != nil { 507 return err 508 } 509 err := wsyscall.Mount("/dev", filepath.Join(*tmpRoot, "dev"), "", 510 wsyscall.MS_BIND, "") 511 if err != nil { 512 return err 513 } 514 err = wsyscall.Mount("/proc", filepath.Join(*tmpRoot, "proc"), "", 515 wsyscall.MS_BIND, "") 516 if err != nil { 517 return err 518 } 519 err = wsyscall.Mount("/sys", filepath.Join(*tmpRoot, "sys"), "", 520 wsyscall.MS_BIND, "") 521 if err != nil { 522 return err 523 } 524 os.Symlink("/proc/mounts", filepath.Join(*tmpRoot, "etc", "mtab")) 525 return nil 526 } 527 528 func listDrives(logger log.DebugLogger) ([]*driveType, error) { 529 basedir := filepath.Join(*sysfsDirectory, "class", "block") 530 file, err := os.Open(basedir) 531 if err != nil { 532 return nil, err 533 } 534 names, err := file.Readdirnames(-1) 535 file.Close() 536 if err != nil { 537 return nil, err 538 } 539 sort.Strings(names) 540 var drives []*driveType 541 for _, name := range names { 542 dirname := filepath.Join(basedir, name) 543 if _, err := os.Stat(filepath.Join(dirname, "partition")); err == nil { 544 logger.Debugf(2, "skipping partition: %s\n", name) 545 continue 546 } 547 if _, err := os.Stat(filepath.Join(dirname, "device")); err != nil { 548 if !os.IsNotExist(err) { 549 return nil, err 550 } 551 logger.Debugf(2, "skipping non-device: %s\n", name) 552 continue 553 } 554 if v, err := readInt(filepath.Join(dirname, "removable")); err != nil { 555 return nil, err 556 } else if v != 0 { 557 logger.Debugf(2, "skipping removable device: %s\n", name) 558 continue 559 } 560 if val, err := readInt(filepath.Join(dirname, "size")); err != nil { 561 return nil, err 562 } else { 563 logger.Debugf(1, "found: %s %d GiB (%d GB)\n", 564 name, val>>21, val<<9/1000000000) 565 drives = append(drives, &driveType{ 566 devpath: filepath.Join("/dev", name), 567 name: name, 568 size: val << 9, 569 }) 570 } 571 } 572 if len(drives) < 1 { 573 return nil, fmt.Errorf("no drives found") 574 } 575 return drives, nil 576 } 577 578 func mount(source string, target string, fstype string, 579 logger log.DebugLogger) error { 580 if *dryRun { 581 logger.Debugf(0, "dry run: skipping mount of %s on %s type=%s\n", 582 source, target, fstype) 583 return nil 584 } 585 logger.Debugf(0, "mount %s on %s type=%s\n", source, target, fstype) 586 if err := os.MkdirAll(target, fsutil.DirPerms); err != nil { 587 return err 588 } 589 return syscall.Mount(source, target, fstype, 0, "") 590 } 591 592 func partitionName(devpath string, partitionNumber int) string { 593 devLeafName := filepath.Base(devpath) 594 partitionName := "p" + strconv.FormatInt(int64(partitionNumber), 10) 595 _, err := os.Stat(filepath.Join("/sys/class/block", devLeafName, 596 devLeafName+partitionName)) 597 if err == nil { 598 return devpath + partitionName 599 } 600 return devpath + strconv.FormatInt(int64(partitionNumber), 10) 601 } 602 603 func readInt(filename string) (uint64, error) { 604 if file, err := os.Open(filename); err != nil { 605 return 0, err 606 } else { 607 defer file.Close() 608 var value uint64 609 if nVal, err := fmt.Fscanf(file, "%d\n", &value); err != nil { 610 return 0, err 611 } else if nVal != 1 { 612 return 0, fmt.Errorf("read %d values, expected 1", nVal) 613 } else { 614 return value, nil 615 } 616 } 617 } 618 619 func remapDevice(device, target string) string { 620 if target == "/" { 621 return device 622 } else { 623 return filepath.Join("/dev/mapper", filepath.Base(device)) 624 } 625 } 626 627 func unmountStorage(logger log.DebugLogger) error { 628 syscall.Sync() 629 time.Sleep(time.Millisecond * 100) 630 file, err := os.Open("/proc/mounts") 631 if err != nil { 632 return err 633 } 634 defer file.Close() 635 var mountPoints []string 636 scanner := bufio.NewScanner(file) 637 for scanner.Scan() { 638 fields := strings.Fields(scanner.Text()) 639 if len(fields) < 2 { 640 continue 641 } else { 642 if strings.HasPrefix(fields[1], *mountPoint) { 643 mountPoints = append(mountPoints, fields[1]) 644 } 645 } 646 } 647 if err := scanner.Err(); err != nil { 648 return err 649 } 650 unmountedMainMountPoint := false 651 for index := len(mountPoints) - 1; index >= 0; index-- { 652 mntPoint := mountPoints[index] 653 if mntPoint == *mountPoint { 654 if err := closeEncryptedVolumes(logger); err != nil { 655 return err 656 } 657 } 658 if err := syscall.Unmount(mntPoint, 0); err != nil { 659 return fmt.Errorf("error unmounting: %s: %s", mntPoint, err) 660 } else { 661 logger.Debugf(2, "unmounted: %s\n", mntPoint) 662 } 663 if mntPoint == *mountPoint { 664 unmountedMainMountPoint = true 665 } 666 } 667 if !unmountedMainMountPoint { 668 return errors.New("did not find main mount point to unmount") 669 } 670 syscall.Sync() 671 return nil 672 } 673 674 func (drive driveType) cryptSetup(cpuSharer cpusharer.CpuSharer, device string, 675 logger log.DebugLogger) error { 676 cpuSharer.GrabCpu() 677 defer cpuSharer.ReleaseCpu() 678 startTime := time.Now() 679 err := run("cryptsetup", *tmpRoot, logger, "--verbose", 680 "--key-file", keyFile, 681 "--cipher", "aes-xts-plain64", "--key-size", "512", 682 "--hash", "sha512", "--iter-time", "5000", "--use-urandom", 683 "luksFormat", device) 684 if err != nil { 685 return err 686 } 687 logger.Printf("formatted encrypted device %s in %s\n", 688 device, time.Since(startTime)) 689 startTime = time.Now() 690 if drive.discarded { 691 err = run("cryptsetup", *tmpRoot, logger, "open", "--type", "luks", 692 "--allow-discards", 693 "--key-file", keyFile, device, filepath.Base(device)) 694 } else { 695 err = run("cryptsetup", *tmpRoot, logger, "open", "--type", "luks", 696 "--key-file", keyFile, device, filepath.Base(device)) 697 } 698 if err != nil { 699 return err 700 } 701 logger.Printf("opened encrypted device %s in %s\n", 702 device, time.Since(startTime)) 703 return nil 704 } 705 706 func (drive driveType) makeFileSystem(cpuSharer cpusharer.CpuSharer, 707 device, target, fstype string, mkfsMutex *sync.Mutex, bytesPerInode uint, 708 logger log.DebugLogger) error { 709 label := target 710 erase := true 711 if label == "/" { 712 label = "rootfs" 713 if drive.discarded { 714 erase = false 715 } 716 } else { 717 if err := drive.cryptSetup(cpuSharer, device, logger); err != nil { 718 return err 719 } 720 device = filepath.Join("/dev/mapper", filepath.Base(device)) 721 } 722 if erase { 723 if err := eraseStart(device, logger); err != nil { 724 return err 725 } 726 } 727 var err error 728 if mkfsMutex != nil { 729 mkfsMutex.Lock() 730 } 731 startTime := time.Now() 732 if bytesPerInode > 0 { 733 err = run("mkfs.ext4", *tmpRoot, logger, 734 "-i", strconv.Itoa(int(bytesPerInode)), "-L", label, 735 "-E", "lazy_itable_init=0,lazy_journal_init=0", device) 736 } else { 737 err = run("mkfs.ext4", *tmpRoot, logger, "-L", label, 738 "-E", "lazy_itable_init=0,lazy_journal_init=0", device) 739 } 740 if mkfsMutex != nil { 741 mkfsMutex.Unlock() 742 } 743 if err != nil { 744 return err 745 } 746 logger.Printf("made file-system on %s in %s\n", 747 device, time.Since(startTime)) 748 return nil 749 } 750 751 func (drive driveType) writeDeviceEntries(device, target, fstype string, 752 fsTab, cryptTab io.Writer, checkOrder uint) error { 753 label := target 754 if label == "/" { 755 label = "rootfs" 756 } else { 757 var options string 758 if drive.discarded { 759 options = "discard" 760 } 761 _, err := fmt.Fprintf(cryptTab, "%-15s %-23s %-15s %s\n", 762 filepath.Base(device), device, keyFile, options) 763 if err != nil { 764 return err 765 } 766 } 767 var fsFlags string 768 if drive.discarded { 769 fsFlags = "discard" 770 } 771 return util.WriteFstabEntry(fsTab, "LABEL="+label, target, fstype, fsFlags, 772 0, checkOrder) 773 } 774 775 func (rebooter kexecRebooter) Reboot() error { 776 return run("kexec", *tmpRoot, rebooter.logger, 777 "-l", rebooter.KernelImageFile, 778 "--append="+rebooter.KernelOptions, 779 "--console-serial", "--serial-baud=115200", 780 "--initrd="+rebooter.InitrdImageFile, 781 "-f") 782 } 783 784 func (rebooter kexecRebooter) String() string { 785 return "kexec" 786 }