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 mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ | ||||||
| RUN apk add --no-cache --initdb -p /out \ | RUN apk add --no-cache --initdb -p /out \ | ||||||
| @@ -6,17 +6,27 @@ RUN apk add --no-cache --initdb -p /out \ | |||||||
|     busybox \ |     busybox \ | ||||||
|     e2fsprogs \ |     e2fsprogs \ | ||||||
|     e2fsprogs-extra \ |     e2fsprogs-extra \ | ||||||
|     jq \ |     btrfs-progs \ | ||||||
|  |     xfsprogs \ | ||||||
|     musl \ |     musl \ | ||||||
|     sfdisk \ |     sfdisk \ | ||||||
|  |     util-linux \ | ||||||
|     && true |     && true | ||||||
| RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache | 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 | FROM scratch | ||||||
| ENTRYPOINT [] | ENTRYPOINT [] | ||||||
| CMD [] | CMD [] | ||||||
| WORKDIR / | WORKDIR / | ||||||
| COPY --from=mirror /out/ / | COPY --from=mirror /out/ / | ||||||
| COPY format.sh / | COPY --from=build /go/bin/format usr/bin/format | ||||||
| CMD ["/bin/sh", "/format.sh"] | CMD ["/usr/bin/format"] | ||||||
| LABEL org.mobyproject.config='{"binds": ["/dev:/dev"], "capabilities": ["CAP_SYS_ADMIN", "CAP_MKNOD"], "net": "new", "ipc": "new"}' | LABEL org.mobyproject.config='{"binds": ["/dev:/dev"], "capabilities": ["CAP_SYS_ADMIN", "CAP_MKNOD"], "net": "new", "ipc": "new"}' | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| IMAGE=format | IMAGE=format | ||||||
| DEPS=format.sh | DEPS=format.go | ||||||
|  |  | ||||||
| include ../package.mk | 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