mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-31 14:45:34 +00:00 
			
		
		
		
	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>
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| FROM linuxkit/alpine:9bcf61f605ef0ce36cc94d59b8eac307862de6e1 AS mirror | ||||
| FROM linuxkit/alpine:488aa6f5dd2d8121a3c5c5c7a1ecf97c424b96ac AS mirror | ||||
|  | ||||
| RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ | ||||
| RUN apk add --no-cache --initdb -p /out \ | ||||
| @@ -6,17 +6,27 @@ RUN apk add --no-cache --initdb -p /out \ | ||||
|     busybox \ | ||||
|     e2fsprogs \ | ||||
|     e2fsprogs-extra \ | ||||
|     jq \ | ||||
|     btrfs-progs \ | ||||
|     xfsprogs \ | ||||
|     musl \ | ||||
|     sfdisk \ | ||||
|     util-linux \ | ||||
|     && true | ||||
| RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache | ||||
|  | ||||
| FROM linuxkit/alpine:488aa6f5dd2d8121a3c5c5c7a1ecf97c424b96ac AS build | ||||
|  | ||||
| RUN apk add --no-cache go musl-dev | ||||
| ENV GOPATH=/go PATH=$PATH:/go/bin | ||||
|  | ||||
| COPY format.go /go/src/format/ | ||||
| RUN go-compile.sh /go/src/format | ||||
|  | ||||
| FROM scratch | ||||
| ENTRYPOINT [] | ||||
| CMD [] | ||||
| WORKDIR / | ||||
| COPY --from=mirror /out/ / | ||||
| COPY format.sh / | ||||
| CMD ["/bin/sh", "/format.sh"] | ||||
| COPY --from=build /go/bin/format usr/bin/format | ||||
| CMD ["/usr/bin/format"] | ||||
| LABEL org.mobyproject.config='{"binds": ["/dev:/dev"], "capabilities": ["CAP_SYS_ADMIN", "CAP_MKNOD"], "net": "new", "ipc": "new"}' | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| IMAGE=format | ||||
| DEPS=format.sh | ||||
| DEPS=format.go | ||||
|  | ||||
| include ../package.mk | ||||
|   | ||||
							
								
								
									
										184
									
								
								pkg/format/format.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								pkg/format/format.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | ||||
| 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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,116 +0,0 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| # this script assumes anything on the disk can be removed if corrupted | ||||
| # other use cases may need different scripts. | ||||
|  | ||||
| # currently only supports ext4 but should be expanded | ||||
|  | ||||
| do_fsck() | ||||
| { | ||||
| 	# preen | ||||
| 	/sbin/e2fsck -p $* | ||||
| 	EXIT_CODE=$? | ||||
| 	# exit code 1 is errors corrected | ||||
| 	[ "${EXIT_CODE}" -eq 1 ] && EXIT_CODE=0 | ||||
| 	# exit code 2 or 3 means need to reboot | ||||
| 	[ "${EXIT_CODE}" -eq 2 -o "${EXIT_CODE}" -eq 3 ] && /sbin/reboot | ||||
| 	# exit code 4 or over is fatal | ||||
| 	[ "${EXIT_CODE}" -lt 4 ] && return "${EXIT_CODE}" | ||||
|  | ||||
| 	# try harder | ||||
| 	/sbin/e2fsck -y $* | ||||
| 	# exit code 1 is errors corrected | ||||
| 	[ "${EXIT_CODE}" -eq 1 ] && EXIT_CODE=0 | ||||
| 	# exit code 2 or 3 means need to reboot | ||||
| 	[ "${EXIT_CODE}" -eq 2 -o "${EXIT_CODE}" -eq 3 ] && /sbin/reboot | ||||
| 	# exit code 4 or over is fatal | ||||
| 	[ "${EXIT_CODE}" -ge 4 ] && printf "Filesystem unrecoverably corrupted, will reformat\n" | ||||
|  | ||||
| 	return "${EXIT_CODE}" | ||||
| } | ||||
|  | ||||
| do_fsck_extend_mount() | ||||
| { | ||||
| 	DRIVE="$1" | ||||
| 	DATA="$2" | ||||
|  | ||||
| 	do_fsck "$DATA" || return 1 | ||||
|  | ||||
| 	# only try to extend if there is a single partition and free space | ||||
| 	PARTITIONS=$(sfdisk -J "$DRIVE" | jq '.partitiontable.partitions | length') | ||||
|  | ||||
| 	if [ "$PARTITIONS" -eq 1 ] && \ | ||||
| 		sfdisk -F "$DRIVE" | grep -q 'Unpartitioned space' && | ||||
| 		! sfdisk -F "$DRIVE" | grep -q '0 B, 0 bytes, 0 sectors' | ||||
| 	then | ||||
| 		SPACE=$(sfdisk -F "$DRIVE" | grep 'Unpartitioned space') | ||||
| 		printf "Resizing disk partition: $SPACE\n" | ||||
|  | ||||
| 		# 83 is Linux partition id | ||||
| 		START=$(sfdisk -J "$DRIVE" | jq -e '.partitiontable.partitions | map(select(.type=="83")) | .[0].start') | ||||
|  | ||||
| 		sfdisk -q --delete "$DRIVE" 2> /dev/null | ||||
| 		echo "${START},,83;" | sfdisk -q "$DRIVE" | ||||
|  | ||||
| 		# set bootable flag | ||||
| 		sfdisk -A "$DRIVE" 1 | ||||
|  | ||||
| 		# update status | ||||
| 		blockdev --rereadpt $diskdev 2> /dev/null | ||||
| 		mdev -s | ||||
|  | ||||
| 		# wait for device | ||||
| 		for i in $(seq 1 50); do test -b "$DATA" && break || sleep .1; mdev -s; done | ||||
|  | ||||
| 		# resize2fs fails unless we use -f here | ||||
| 		do_fsck -f "$DATA" || return 1 | ||||
| 		resize2fs "$DATA" | ||||
|  | ||||
| 		do_fsck "$DATA" || return 1 | ||||
| 	fi | ||||
| } | ||||
|  | ||||
| do_mkfs() | ||||
| { | ||||
| 	diskdev="$1" | ||||
|  | ||||
| 	# new disks does 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 | ||||
| 	echo "w" | fdisk $diskdev >/dev/null | ||||
|  | ||||
| 	# format one large partition | ||||
| 	echo ";" | sfdisk --quiet $diskdev | ||||
|  | ||||
| 	# update status | ||||
| 	blockdev --rereadpt $diskdev 2> /dev/null | ||||
|  | ||||
| 	# wait for device | ||||
| 	for i in $(seq 1 50); do test -b "$DATA" && break || sleep .1; mdev -s; done | ||||
|  | ||||
| 	FSOPTS="-O resize_inode,has_journal,extent,huge_file,flex_bg,uninit_bg,64bit,dir_nlink,extra_isize" | ||||
|  | ||||
| 	mkfs.ext4 -q -F $FSOPTS ${diskdev}1 | ||||
| } | ||||
|  | ||||
| # TODO fix for multiple disks, cdroms etc | ||||
| DEV="$(find /dev -maxdepth 1 -type b ! -name 'loop*' | grep -v '[0-9]$' | sed 's@.*/dev/@@' | sort | head -1 )" | ||||
|  | ||||
| [ -z "${DEV}" ] && exit 1 | ||||
|  | ||||
| DRIVE="/dev/${DEV}" | ||||
|  | ||||
| # see if it has a partition table already | ||||
| if sfdisk -d "${DRIVE}" >/dev/null 2>/dev/null | ||||
| then | ||||
| 	DATA=$(sfdisk -J "$DRIVE" | jq -e -r '.partitiontable.partitions | map(select(.type=="83")) | .[0].node') | ||||
| 	if [ $? -eq 0 ] | ||||
| 	then | ||||
| 		do_fsck_extend_mount "$DRIVE" "$DATA" || do_mkfs "$DRIVE" | ||||
| 	else | ||||
| 		do_mkfs "$DRIVE" | ||||
| 	fi | ||||
| else | ||||
| 	do_mkfs "$DRIVE" | ||||
| fi | ||||
		Reference in New Issue
	
	Block a user