Files
linuxkit/pkg/format/format.go
Dave Tucker 5273ec1d33 format: Add partition labels and new filesystems
This commit re-writes the format package in Go and adds the ability to
add partition labels and also specify either ext4, btrfs or xfs

Signed-off-by: Dave Tucker <dt@docker.com>
2017-07-26 13:26:39 +01:00

185 lines
4.2 KiB
Go

package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strings"
"syscall"
"time"
)
const (
timeout = 60
ext4opts = "resize_inode,has_journal,extent,huge_file,flex_bg,uninit_bg,64bit,dir_nlink,extra_isize"
)
var (
labelVar string
fsTypeVar string
drives map[string]bool
driveKeys []string
)
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
}
first = d
break
}
if first == "" {
return fmt.Errorf("No eligible disks found")
}
if err := format(first, label, fsType); err != nil {
return err
}
return nil
}
func format(d, label, fsType string) error {
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
fdisk create that DOS signature, by just do a "w", a write.
http://bugs.alpinelinux.org/issues/145
*/
fdiskCmd := exec.Command("fdisk", d)
fdiskCmd.Stdin = strings.NewReader("w")
if out, err := fdiskCmd.CombinedOutput(); err != nil {
return fmt.Errorf("Error running fdisk: %v\n%s", err, out)
}
// format one large partition
partCmd := exec.Command("sfdisk", "--quiet", d)
partCmd.Stdin = strings.NewReader(";")
if out, err := partCmd.CombinedOutput(); err != nil {
return fmt.Errorf("Error running sfdisk: %v\n%s", err, out)
}
// update status
if err := exec.Command("blockdev", "--rereadpt", d).Run(); err != nil {
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 !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}
switch fsType {
case "ext4":
ext4Args := []string{"-F", "-O", ext4opts}
if label != "" {
ext4Args = append(ext4Args, []string{"-L", label}...)
}
mkfsArgs = append(mkfsArgs, ext4Args...)
case "btrfs":
btrfsArgs := []string{"-f"}
if label != "" {
btrfsArgs = append(btrfsArgs, []string{"-l", label}...)
}
mkfsArgs = append(mkfsArgs, btrfsArgs...)
case "xfs":
xfsArgs := []string{"-f"}
if label != "" {
xfsArgs = append(xfsArgs, []string{"-L", label}...)
}
mkfsArgs = append(mkfsArgs, xfsArgs...)
default:
log.Println("WARNING: Unsupported filesystem.")
}
mkfsArgs = append(mkfsArgs, partition)
if out, err := exec.Command("mkfs", mkfsArgs...).CombinedOutput(); err != nil {
return fmt.Errorf("Error running mkfs: %v\n%s", err, string(out))
}
log.Printf("Partition %s successfully created!", partition)
return nil
}
// return a list of all available drives
func findDrives() {
drives = make(map[string]bool)
driveKeys = []string{}
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 {
continue
}
// ignore if it matches regexp
if ignoreExp.MatchString(d.Name()) {
continue
}
driveKeys = append(driveKeys, filepath.Join("/dev", d.Name()))
}
sort.Strings(driveKeys)
for _, d := range driveKeys {
drives[d] = true
}
}
func init() {
flag.StringVar(&labelVar, "label", "", "Disk label to apply")
flag.StringVar(&fsTypeVar, "type", "ext4", "Type of filesystem to create")
}
func main() {
flag.Parse()
findDrives()
if flag.NArg() > 1 {
log.Fatalf("Too many arguments provided")
}
if flag.NArg() == 0 {
if err := autoformat(labelVar, fsTypeVar); err != nil {
log.Fatalf("%v", err)
}
} else {
if err := format(flag.Args()[0], labelVar, fsTypeVar); err != nil {
log.Fatalf("%v", err)
}
}
}