mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-09-20 00:27:27 +00:00
format: blkid content check, format argument, and verbose argument
- Added new argument -force to the format utility. This will force formatting of the specified single device if it exists and is a block device - By default, the format package will no longer format the specified single device if content exists on that device - Added new blkid-based check for content, in addition to the existing check for partitions on autoformat candidate devices. - Cleanup of old unused code. - Refactoring of block device verification. - Added test/cases/040_packages/006_format_mount/005_by_device_force - Added document of new arguments to /docs/external-disk.md. Also sorted the arguments in the doc alphabetically and added them as bullets so they do not run together on the page. Signed-off-by: Isaac Rodman <isaac@eyz.us>
This commit is contained in:
committed by
Isaac Rodman
parent
367c7e2c88
commit
3de78f2109
@@ -1,4 +1,4 @@
|
||||
FROM linuxkit/alpine:87a0cd10449d72f374f950004467737dbf440630 AS mirror
|
||||
FROM linuxkit/alpine:d1778ee29f11475548f0e861f57005a84e82ba4e AS mirror
|
||||
|
||||
RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/
|
||||
RUN apk add --no-cache --initdb -p /out \
|
||||
@@ -11,10 +11,11 @@ RUN apk add --no-cache --initdb -p /out \
|
||||
musl \
|
||||
sfdisk \
|
||||
util-linux \
|
||||
blkid \
|
||||
&& true
|
||||
RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache
|
||||
|
||||
FROM linuxkit/alpine:87a0cd10449d72f374f950004467737dbf440630 AS build
|
||||
FROM linuxkit/alpine:d1778ee29f11475548f0e861f57005a84e82ba4e AS build
|
||||
|
||||
RUN apk add --no-cache go musl-dev
|
||||
ENV GOPATH=/go PATH=$PATH:/go/bin
|
||||
|
@@ -9,7 +9,6 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -21,36 +20,127 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
labelVar string
|
||||
fsTypeVar string
|
||||
drives map[string]bool
|
||||
driveKeys []string
|
||||
labelVar string
|
||||
fsTypeVar string
|
||||
forceVar bool
|
||||
verboseVar bool
|
||||
drives map[string]bool
|
||||
driveKeys []string
|
||||
)
|
||||
|
||||
func hasPartitions(d string) bool {
|
||||
err := exec.Command("sfdisk", "-d", d).Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func isEmptyDevice(d string) (bool, error) {
|
||||
// default result
|
||||
isEmpty := false
|
||||
|
||||
if verboseVar {
|
||||
log.Printf("Checking if %s is empty", d)
|
||||
}
|
||||
|
||||
out, err := exec.Command("blkid", d).Output()
|
||||
if err == nil {
|
||||
log.Printf("%s has content. blkid returned: %s", d, out)
|
||||
// there is content, so exit early
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
// blkid exitcode 2 (from the non-busybox version) signifies the block device has no detectable content signatures
|
||||
if status.ExitStatus() == 2 {
|
||||
if verboseVar {
|
||||
log.Printf("blkid did not find any existing content on %s.", d)
|
||||
}
|
||||
// no content detected, but continue through all checks
|
||||
isEmpty = true
|
||||
} else {
|
||||
return isEmpty, fmt.Errorf("Could not determine if %s was empty. blkid %s returned: %s (exitcode %d)", d, d, out, status.ExitStatus())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasPartitions(d) {
|
||||
log.Printf("Partition table found on device %s. Skipping.", d)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// return final result
|
||||
return isEmpty, nil
|
||||
}
|
||||
|
||||
func autoformat(label, fsType string) error {
|
||||
var first string
|
||||
for _, d := range driveKeys {
|
||||
err := exec.Command("sfdisk", "-d", d).Run()
|
||||
if err == nil {
|
||||
log.Printf("Partition table found on device %s. Skipping.", d)
|
||||
continue
|
||||
if verboseVar {
|
||||
log.Printf("Considering auto format for device %s", d)
|
||||
}
|
||||
// break the loop with the first empty device we find
|
||||
isEmpty, err := isEmptyDevice(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isEmpty == true {
|
||||
first = d
|
||||
break
|
||||
}
|
||||
first = d
|
||||
break
|
||||
}
|
||||
|
||||
if first == "" {
|
||||
return fmt.Errorf("No eligible disks found")
|
||||
}
|
||||
|
||||
if err := format(first, label, fsType); err != nil {
|
||||
return err
|
||||
return format(first, label, fsType, false)
|
||||
}
|
||||
|
||||
func refreshDevicesAndWaitFor(awaitedDevice string) error {
|
||||
exec.Command("mdev", "-s").Run()
|
||||
|
||||
// wait for device
|
||||
var done bool
|
||||
for i := 0; i < timeout; i++ {
|
||||
stat, err := os.Stat(awaitedDevice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isBlockDevice(&stat) {
|
||||
done = true
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
exec.Command("mdev", "-s").Run()
|
||||
}
|
||||
if !done {
|
||||
return fmt.Errorf("Error waiting for device %s", awaitedDevice)
|
||||
}
|
||||
// even after the device appears we still have a race
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func format(d, label, fsType string) error {
|
||||
func format(d, label, fsType string, forced bool) error {
|
||||
if forced {
|
||||
// clear partitions on device if forced format and they exist
|
||||
if hasPartitions(d) {
|
||||
if verboseVar {
|
||||
log.Printf("Clearing partitions on %s because forced format was requested", d)
|
||||
}
|
||||
partCmd := exec.Command("sfdisk", "--quiet", "--delete", d)
|
||||
partCmd.Stdin = strings.NewReader(";")
|
||||
if out, err := partCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("Error deleting partitions with sfdisk: %v\n%s", err, out)
|
||||
}
|
||||
} else {
|
||||
if verboseVar {
|
||||
log.Printf("No need to clear partitions.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Creating partition on %s", d)
|
||||
/* new disks do not have an DOS signature in sector 0
|
||||
this makes sfdisk complain. We can workaround this by letting
|
||||
@@ -75,28 +165,10 @@ func format(d, label, fsType string) error {
|
||||
return fmt.Errorf("Error running blockdev: %v", err)
|
||||
}
|
||||
|
||||
exec.Command("mdev", "-s").Run()
|
||||
|
||||
partition := fmt.Sprintf("%s1", d)
|
||||
// wait for device
|
||||
var done bool
|
||||
for i := 0; i < timeout; i++ {
|
||||
stat, err := os.Stat(partition)
|
||||
if err == nil {
|
||||
mode := stat.Sys().(*syscall.Stat_t).Mode
|
||||
if (mode & syscall.S_IFMT) == syscall.S_IFBLK {
|
||||
done = true
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
exec.Command("mdev", "-s").Run()
|
||||
if err := refreshDevicesAndWaitFor(partition); err != nil {
|
||||
return err
|
||||
}
|
||||
if !done {
|
||||
return fmt.Errorf("Error waiting for device %s", partition)
|
||||
}
|
||||
// even after the device appears we still have a race
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// mkfs
|
||||
mkfsArgs := []string{"-t", fsType}
|
||||
@@ -133,6 +205,13 @@ func format(d, label, fsType string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isBlockDevice(d *os.FileInfo) bool {
|
||||
// this probably shouldn't be so hard
|
||||
// but d.Mode()&os.ModeDevice == 0 doesn't work as expected
|
||||
mode := (*d).Sys().(*syscall.Stat_t).Mode
|
||||
return (mode & syscall.S_IFMT) == syscall.S_IFBLK
|
||||
}
|
||||
|
||||
// return a list of all available drives
|
||||
func findDrives() {
|
||||
drives = make(map[string]bool)
|
||||
@@ -140,45 +219,76 @@ func findDrives() {
|
||||
ignoreExp := regexp.MustCompile(`^loop.*$|^nbd.*$|^[a-z]+[0-9]+$`)
|
||||
devs, _ := ioutil.ReadDir("/dev")
|
||||
for _, d := range devs {
|
||||
// this probably shouldn't be so hard
|
||||
// but d.Mode()&os.ModeDevice == 0 doesn't work as expected
|
||||
mode := d.Sys().(*syscall.Stat_t).Mode
|
||||
if (mode & syscall.S_IFMT) != syscall.S_IFBLK {
|
||||
if isBlockDevice(&d) {
|
||||
if verboseVar {
|
||||
log.Printf("/dev/%s is a block device", d.Name())
|
||||
}
|
||||
} else {
|
||||
if verboseVar {
|
||||
log.Printf("/dev/%s is not a block device", d.Name())
|
||||
}
|
||||
continue
|
||||
}
|
||||
// ignore if it matches regexp
|
||||
if ignoreExp.MatchString(d.Name()) {
|
||||
if verboseVar {
|
||||
log.Printf("ignored device /dev/%s during drive autodetection", d.Name())
|
||||
}
|
||||
continue
|
||||
}
|
||||
driveKeys = append(driveKeys, filepath.Join("/dev", d.Name()))
|
||||
}
|
||||
sort.Strings(driveKeys)
|
||||
for _, d := range driveKeys {
|
||||
drives[d] = true
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&forceVar, "force", false, "Force format of specified single device (default false)")
|
||||
flag.StringVar(&labelVar, "label", "", "Disk label to apply")
|
||||
flag.StringVar(&fsTypeVar, "type", "ext4", "Type of filesystem to create")
|
||||
flag.BoolVar(&verboseVar, "verbose", false, "Enable verbose output (default false)")
|
||||
}
|
||||
|
||||
func verifyBlockDevice(device string) error {
|
||||
d, err := os.Stat(device)
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("%s does not exist", device)
|
||||
}
|
||||
if !isBlockDevice(&d) {
|
||||
return fmt.Errorf("%s is not a block device", device)
|
||||
}
|
||||
// passed checks
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
findDrives()
|
||||
|
||||
if flag.NArg() > 1 {
|
||||
log.Fatalf("Too many arguments provided")
|
||||
}
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
// auto-detect drives if a device to format is not explicitly specified
|
||||
findDrives()
|
||||
if err := autoformat(labelVar, fsTypeVar); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
} else {
|
||||
if err := format(flag.Args()[0], labelVar, fsTypeVar); err != nil {
|
||||
candidateDevice := flag.Args()[0]
|
||||
|
||||
if err := verifyBlockDevice(candidateDevice); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
if forceVar == true {
|
||||
if err := format(candidateDevice, labelVar, fsTypeVar, forceVar); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
} else {
|
||||
// add the deviceVar to the array of devices to consider autoformatting
|
||||
driveKeys = []string{candidateDevice}
|
||||
if err := autoformat(labelVar, fsTypeVar); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user