Formatting USB disks

Here is a simple shell script that formats /dev/da type devices with either a UFS (Unix File System) or the good old FAT (File Allocation Table). One should note that the FAT32 implementation is buggy. That is to say that formatting it under FreeBSD works fine, however, when using a pen drive under Windows it is not possible to write to the device. Only reading is supported. For this reason, we are here formatting a disk with FAT16.


The respective man pages for the most important commands used are:


dd(1) which is used here to clear just the first megabyte of the disk.


sysctl(8) needed here to allow us to clear a portion of the disk using dd(1).


fdisk(8) create a UFS or FAT slice (or possibly even another type of slice) on the disk before formatting.


bsdlabel(8) used to write a bsdlabel to a disk.


newfs(8) with this command the disk is actually formatted with a UFS.


newfs_msdos(8) used to format a disk with FAT.


And here goes the script:


format_disk.sh


#!/bin/sh

# variables
WHO=`whoami`
DEVICE=

sanity_checks()
{
	local device path

	# are we root?
	if [ ${WHO} != "root" ] ; then
		echo "you need to be root to format the disk"
		exit 1
	fi

	# Check wether or not the device has been specified with its full path:
	# /dev/device. If not prepend it.

	path=`awk -v string_to_check=$1 'BEGIN { print index(string_to_check, "/dev/") }'`

	if [ $path -eq 0 ]; then
		device=/dev/$1		# prepend /dev/
	else
		device=$1
	fi

	# does the device special file exist?
	if [ ! -c ${device} ] ; then
		echo "device ${device} does not exist"
		exit 1
	fi

	# are we operating on a slice of a device or the device itself?
	# if it is a device (e.g. /dev/da0) all is ok. if it's the slice of 
	# a given device extract the device and continue.

	case "${device}" in

	/dev/da[0-9])
		echo "$device"
		;;

	/dev/da[0-9]s[0-9])
	        echo "you specified a slice, using the device instead" >&2
 		# extract the device part
		device=`echo $device | sed s/s[0-9]//`
		echo "$device"
		;;

	*)
		echo "${device} is not a /dev/da device"
		exit 1
		;;
	esac
}

confirm()
{
	local device confirm

	device=$1

	echo "Do you really want to format the device: \"${device}\"?"
	echo -n "All data will be lost (yes/no): "
	read -t 30 confirm		# we have exactly 30 seconds to make a choice

	case $confirm in 

	[Yy][Ee][Ss])
		return
		;;

	[Nn][Oo])
		echo "aborted"
		exit 0
		;;

	*)
		echo "aborted"
		exit 1
		;;
	esac
}

format_ufs_2()
{
	local device slice

	device=$1 ; slice=${device}s1

	echo "### formatting ${device} with ufs 2 ###"

	# enter destructive mode
	sysctl kern.geom.debugflags=16

	# zero the first MB using dd(1)
	dd if=/dev/zero of=${device} bs=1m count=1

	# leave destructive mode
	sysctl kern.geom.debugflags=0

	# use fdisk(8) to create a partition
	fdisk -B -I ${device}		# the B option makes the device bootable

	# bsdlabel(8)
	bsdlabel -w -B ${slice}

	# use newfs(8) to format the disk via ufs2
	newfs -O 2 -U ${slice}		# -U enables soft updates (whatever that may mean)
}

format_msdosfs()
{
	local device slice

	device=$1 ; slice=${device}s1

	echo "### formatting ${device} with msdosfs ###"
	
	# enter destructive mode
	sysctl kern.geom.debugflags=16

	# zero the first MB using dd(1)
	dd if=/dev/zero of=${device} bs=1m count=1

	# leave destructive mode
	sysctl kern.geom.debugflags=0

	# interactive mode
	fdisk -i ${device}		# choose partition type 12

	# create a new fat filesystem via newfs_msdos(8)
	newfs_msdos -F 16 ${slice} 	# fat 32 is buggy
}

usage()
{
	echo "usage: format_disk.sh ufs|msdosfs device"
	exit 1
}

#######################################################################
### entry point

if [ $# -ne 2 ] ; then
	usage
fi

if [ "$1" != "ufs" ] && [ "$1" != "msdosfs" ] ; then
	usage
fi

# sanity checks
DEVICE=$(sanity_checks $2)

# confirm
confirm $DEVICE

# go
if [ "$1" = "ufs" ] ; then
	format_ufs_2 ${DEVICE}
elif [ "$1" = "msdosfs" ] ; then
	format_msdosfs ${DEVICE}
fi

echo "Formatting complete!"
exit 0

This shell script can only be run as root, as this requirement is checked at the beginning of the script via the sanity_checks function. To make it executable run:



# chmod +x format_disk.sh

After which you can issue:



# ./format_disk.sh ufs /dev/da1

Or even



# ./format_disk.sh ufs da1

to format the disk, connected at point "/dev/da1" with a Unix File System.


The script makes sure, the correct device specified on the command line is spoken to, even if we are indicating the slice of a device such as /dev/da0s1 in which case the sanity_checks function will extract the device itself, shortening it to /dev/da0.


This script is mainly used to format USB drives. Formatting other types of drives could be implemented, however this could be the aim of another script.