github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/chroot/device.go (about) 1 package chroot 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 ) 10 11 // AvailableDevice finds an available device and returns it. Note that 12 // you should externally hold a flock or something in order to guarantee 13 // that this device is available across processes. 14 func AvailableDevice() (string, error) { 15 prefix, err := devicePrefix() 16 if err != nil { 17 return "", err 18 } 19 20 letters := "fghijklmnop" 21 for _, letter := range letters { 22 device := fmt.Sprintf("/dev/%s%c", prefix, letter) 23 24 // If the block device itself, i.e. /dev/sf, exists, then we 25 // can't use any of the numbers either. 26 if _, err := os.Stat(device); err == nil { 27 continue 28 } 29 30 // To be able to build both Paravirtual and HVM images, the unnumbered 31 // device and the first numbered one must be available. 32 // E.g. /dev/xvdf and /dev/xvdf1 33 numbered_device := fmt.Sprintf("%s%d", device, 1) 34 if _, err := os.Stat(numbered_device); err != nil { 35 return device, nil 36 } 37 } 38 39 return "", errors.New("available device could not be found") 40 } 41 42 // devicePrefix returns the prefix ("sd" or "xvd" or so on) of the devices 43 // on the system. 44 func devicePrefix() (string, error) { 45 available := []string{"sd", "xvd"} 46 47 f, err := os.Open("/sys/block") 48 if err != nil { 49 return "", err 50 } 51 defer f.Close() 52 53 dirs, err := f.Readdirnames(-1) 54 if dirs != nil && len(dirs) > 0 { 55 for _, dir := range dirs { 56 dirBase := filepath.Base(dir) 57 for _, prefix := range available { 58 if strings.HasPrefix(dirBase, prefix) { 59 return prefix, nil 60 } 61 } 62 } 63 } 64 65 if err != nil { 66 return "", err 67 } 68 69 return "", errors.New("device prefix could not be detected") 70 }