Skip to content

Commit

Permalink
Add new options: '-b' to override block size, '-f' to force non-inter…
Browse files Browse the repository at this point in the history
…active, '-p' for partition type, '-w' to optionally wipe entire device. Update usage. Update README.
  • Loading branch information
JElchison committed Feb 5, 2016
1 parent 94b3656 commit 107cf1c
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 38 deletions.
55 changes: 48 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,50 @@ Simply copy format-udf.sh to a directory of your choosing. Don't forget to make

# Usage
```
./format-udf.sh <drive> <label>
```
Example:
```
./format-udf.sh sda "My UDF External Drive"
Usage: ./format-udf.sh [-b BLOCK_SIZE] [-f] [-p PARTITION_TYPE] [-w WIPE_METHOD] drive label
-b BLOCK_SIZE
Block size to be used during format operation.
If absent, defaults to value reported by blockdev/diskutil.
This is useful in light of the following Linux kernel bug:
https://bugzilla.kernel.org/show_bug.cgi?id=102271
See also:
https://github.com/JElchison/format-udf/issues/13
-f
Forces non-interactive mode. Useful for scripting.
Please use with caution, as no user confirmation is given.
-p PARTITION_TYPE
Partition type to set during format operation.
Currently supported types include: mbr, none
mbr - Master boot record (default)
none - Do not modify partitions
If absent, defaults to 'mbr'.
See also:
https://github.com/JElchison/format-udf#a-fake-partition-table-to-fake-out-windows
-w WIPE_METHOD
Wipe method to be used before format operation.
Currently supported types include: quick, zero, scrub
quick - Quick method (default)
zero - Write zeros to the entire drive
scrub - Iteratively writes patterns on drive
to make retrieving the data more difficult.
Requires 'scrub' to be executable and in the PATH.
See also http://linux.die.net/man/1/scrub
If absent, defaults to 'quick'.
Note: 'zero' and 'scrub' methods will take a long time.
drive
Drive to format. Should be of the form:
* sdx (Linux, where 'x' is a letter) or
* diskN (OS X, where 'N' is a number)
label
Label to apply to formatted drive.
Example: ./format-udf.sh sda "My External Drive"
```


Expand All @@ -87,6 +126,7 @@ user@computer:~$ ./format-udf.sh sdg "My UDF External Drive"
[+] Looking for drive listing tool... using /sbin/blockdev
[+] Looking for unmount tool... using /bin/umount
[+] Looking for UDF tool... using /usr/bin/mkudffs
[+] Parsing options...
[+] Validating arguments...
[+] Gathering drive information...
[sudo] password for user:
Expand Down Expand Up @@ -127,7 +167,7 @@ start=195371567, blocks=1, type=ANCHOR
2+0 records in
2+0 records out
2 bytes (2 B) copied, 0.000108835 s, 18.4 kB/s
[*] Successfully formatted /dev/sdg: LABEL="My UDF External Drive" TYPE="udf"
[+] Successfully formatted /dev/sdg: LABEL="My UDF External Drive" TYPE="udf"
Please disconnect/reconnect your drive now.
```

Expand All @@ -138,6 +178,7 @@ computer:~ user$ ./format-udf.sh disk2 "My UDF External Drive"
[+] Looking for drive listing tool... using /usr/sbin/diskutil
[+] Looking for unmount tool... using /usr/sbin/diskutil
[+] Looking for UDF tool... using /sbin/newfs_udf
[+] Parsing options...
[+] Validating arguments...
[+] Gathering drive information...
/dev/disk2
Expand Down Expand Up @@ -167,7 +208,7 @@ write to block device: /dev/disk2 last written block address: 195371567
2+0 records in
2+0 records out
2 bytes transferred in 0.000602 secs (3322 bytes/sec)
[*] Successfully formatted
[+] Successfully formatted
Please disconnect/reconnect your drive now.
```

Expand Down
220 changes: 189 additions & 31 deletions format-udf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
#
# Bash script to format a block drive (hard drive or Flash drive) in UDF. The output is a drive that can be used for reading/writing across multiple operating system families: Windows, OS X, and Linux. This script should be capable of running in OS X or in Linux.
#
# Version 1.1.2
# Version 1.2.0
#
# Copyright (C) 2015 Jonathan Elchison <[email protected]>
# Copyright (C) 2016 Jonathan Elchison <[email protected]>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -35,6 +35,8 @@ set -euf -o pipefail
HPC=255
# maximum number of sectors per track
SPT=63
# reset in case getopts has been used previously in the shell
OPTIND=1


###############################################################################
Expand All @@ -47,8 +49,52 @@ SPT=63
# Returns:
# None
print_usage() {
echo "Usage: $0 <drive> <label>" >&2
echo "Example: $0 sda \"My External Drive\"" >&2
cat <<EOF >&2
Usage: $0 [-b BLOCK_SIZE] [-f] [-p PARTITION_TYPE] [-w WIPE_METHOD] drive label
-b BLOCK_SIZE
Block size to be used during format operation.
If absent, defaults to value reported by blockdev/diskutil.
This is useful in light of the following Linux kernel bug:
https://bugzilla.kernel.org/show_bug.cgi?id=102271
See also:
https://github.com/JElchison/format-udf/issues/13
-f
Forces non-interactive mode. Useful for scripting.
Please use with caution, as no user confirmation is given.
-p PARTITION_TYPE
Partition type to set during format operation.
Currently supported types include: mbr, none
mbr - Master boot record (default)
none - Do not modify partitions
If absent, defaults to 'mbr'.
See also:
https://github.com/JElchison/format-udf#a-fake-partition-table-to-fake-out-windows
-w WIPE_METHOD
Wipe method to be used before format operation.
Currently supported types include: quick, zero, scrub
quick - Quick method (default)
zero - Write zeros to the entire drive
scrub - Iteratively writes patterns on drive
to make retrieving the data more difficult.
Requires 'scrub' to be executable and in the PATH.
See also http://linux.die.net/man/1/scrub
If absent, defaults to 'quick'.
Note: 'zero' and 'scrub' methods will take a long time.
drive
Drive to format. Should be of the form:
* sdx (Linux, where 'x' is a letter) or
* diskN (OS X, where 'N' is a number)
label
Label to apply to formatted drive.
Example: $0 sda "My External Drive"
EOF
}

# Prints hex representation of CHS (cylinder-head-sector) to stdout
Expand Down Expand Up @@ -202,6 +248,73 @@ fi
echo " using $TOOL_UDF"


###############################################################################
# set default options
###############################################################################

ARG_BLOCK_SIZE=
FORCE=
PARTITION_TYPE=mbr
WIPE_METHOD=quick


###############################################################################
# parse options
###############################################################################

echo "[+] Parsing options..."

while getopts "b:fp:w:" opt; do
case $opt in
b)
ARG_BLOCK_SIZE="$OPTARG"
# no need to validate this here, as BLOCK_SIZE is validated below (before anything destructive happens)
;;
f)
FORCE=1
;;
p)
PARTITION_TYPE="$OPTARG"
if [[ "$PARTITION_TYPE" != "mbr" ]] &&
[[ "$PARTITION_TYPE" != "none" ]]; then
echo "[-] Invalid partition type: $PARTITION_TYPE" >&2
print_usage
exit 1
fi
;;
w)
WIPE_METHOD="$OPTARG"
if [[ "$WIPE_METHOD" != "quick" ]] &&
[[ "$WIPE_METHOD" != "zero" ]] &&
[[ "$WIPE_METHOD" != "scrub" ]]; then
echo "[-] Invalid wipe method: $WIPE_METHOD" >&2
print_usage
exit 1
fi
if [[ "$WIPE_METHOD" = "scrub" ]]; then
if [[ ! -x $(which scrub) ]]; then
echo "[-] Dependencies unmet. Please verify that the following are installed, executable, and in the PATH: scrub" >&2
exit 1
fi
fi
;;
\?)
echo "[-] Invalid option: -$OPTARG" >&2
print_usage
exit 1
;;
:)
echo "[-] Option -$OPTARG requires an argument" >&2
print_usage
exit 1
;;
esac
done

shift $((OPTIND-1))
([[ "$1" = "--" ]] 2>/dev/null && shift) || true


###############################################################################
# validate arguments
###############################################################################
Expand All @@ -228,7 +341,7 @@ mount /dev/$DEVICE 2>/dev/null || true


###############################################################################
# verify with user
# print drive information
###############################################################################

echo "[+] Gathering drive information..."
Expand All @@ -243,14 +356,20 @@ else
exit 1
fi

# give the user a chance to realize his/her mistake
echo "The above-listed drive (and partitions, if any) will be completely erased."
# TODO add '-f' command-line option to bypass this interactive step
read -p "Type 'yes' if this is what you intend: " YES_CASE
YES=$(echo $YES_CASE | tr '[:upper:]' '[:lower:]')
if [[ $YES != "yes" ]]; then
echo "[-] Exiting without changes to /dev/$DEVICE." >&2
exit 1

###############################################################################
# verify with user
###############################################################################

if [[ -z $FORCE ]]; then
# give the user a chance to realize his/her mistake
echo "The above-listed drive (and partitions, if any) will be completely erased."
read -p "Type 'yes' if this is what you intend: " YES_CASE
YES=$(echo $YES_CASE | tr '[:upper:]' '[:lower:]')
if [[ $YES != "yes" ]]; then
echo "[-] Exiting without changes to /dev/$DEVICE." >&2
exit 1
fi
fi


Expand Down Expand Up @@ -279,21 +398,26 @@ echo "[+] Validating detected total size..."
# gather information - physical block size
###############################################################################

echo "[+] Detecting physical block size..."
if [[ $TOOL_DRIVE_LISTING = $TOOL_BLOCKDEV ]]; then
BLOCK_SIZE=$(sudo blockdev --getpbsz /dev/$DEVICE)
elif [[ -x $TOOL_DISKUTIL ]]; then
BLOCK_SIZE=$(diskutil info $DEVICE | grep -i 'Device Block Size' | awk -F ':' '{print $2}' | awk '{print $1}')
if [[ -z $ARG_BLOCK_SIZE ]]; then
echo "[+] Detecting physical block size..."
if [[ $TOOL_DRIVE_LISTING = $TOOL_BLOCKDEV ]]; then
BLOCK_SIZE=$(sudo blockdev --getpbsz /dev/$DEVICE)
elif [[ -x $TOOL_DISKUTIL ]]; then
BLOCK_SIZE=$(diskutil info $DEVICE | grep -i 'Device Block Size' | awk -F ':' '{print $2}' | awk '{print $1}')
else
echo "[-] Cannot detect physical block size" >&2
exit 1
fi
else
echo "[-] Cannot detect physical block size" >&2
exit 1
echo "[+] Overriding physical block size..."
BLOCK_SIZE=$ARG_BLOCK_SIZE
fi
echo "[*] Using block size of $BLOCK_SIZE"

# validate that $BLOCK_SIZE is numeric > 0
echo "[+] Validating detected block size..."
(echo "$BLOCK_SIZE" | egrep -q '^[0-9]+$') || (echo "[-] Could not detect valid block size. Exiting without changes to /dev/$DEVICE." >&2 && false)
[[ $BLOCK_SIZE -gt 0 ]] || (echo "[-] Could not detect valid block size. Exiting without changes to /dev/$DEVICE." >&2 && false)
(echo "$BLOCK_SIZE" | egrep -q '^[0-9]+$') || (echo "[-] Invalid block size. Exiting without changes to /dev/$DEVICE." >&2 && false)
[[ $BLOCK_SIZE -gt 0 ]] || (echo "[-] Invalid block size. Exiting without changes to /dev/$DEVICE." >&2 && false)


###############################################################################
Expand All @@ -314,7 +438,30 @@ fi


###############################################################################
# zero out partition table
# optionally wipe drive
###############################################################################

case $WIPE_METHOD in
quick)
# nothing to do
;;
zero)
echo "[+] Overwriting drive with zeros. This will likely take a LONG time..."
sudo dd if=/dev/zero of=/dev/$DEVICE bs=$BLOCK_SIZE || true
;;
scrub)
echo "[+] Scrubbing drive with random patterns. This will likely take a LONG time..."
sudo scrub -f /dev/$DEVICE
;;
*)
echo "[-] Internal error 3" >&2
exit 1
;;
esac


###############################################################################
# zero out partition table (required even without fake partition table)
###############################################################################

echo "[+] Zeroing out any existing partition table on drive..."
Expand Down Expand Up @@ -344,20 +491,31 @@ elif [[ $TOOL_UDF = $TOOL_NEWFS_UDF ]]; then
# --enc - encode volume name in UTF8
(sudo newfs_udf -b $BLOCK_SIZE -m blk -t ow -r 2.01 -v "$LABEL" --enc utf8 /dev/$DEVICE) || (echo "[-] Format failed!" >&2 && false)
else
echo "[-] Internal error 3" >&2
echo "[-] Internal error 4" >&2
exit 1
fi


###############################################################################
# write fake MBR (for added compatibility on Windows)
# write fake partition table (for added compatibility on Windows)
###############################################################################

echo "[+] Writing fake MBR..."
# first block has already been zero'd. start by writing the (only) partition entry at its correct offset.
entire_disk_partition_entry $TOTAL_SIZE $BLOCK_SIZE | xxd -r -p | sudo dd of=/dev/$DEVICE bs=1 seek=446 count=16
# Boot signature at the end of the block
echo -n 55aa | xxd -r -p | sudo dd of=/dev/$DEVICE bs=1 seek=510 count=2
case $PARTITION_TYPE in
none)
# nothing to do
;;
mbr)
echo "[+] Writing fake MBR..."
# first block has already been zero'd. start by writing the (only) partition entry at its correct offset.
entire_disk_partition_entry $TOTAL_SIZE $BLOCK_SIZE | xxd -r -p | sudo dd of=/dev/$DEVICE bs=1 seek=446 count=16
# Boot signature at the end of the block
echo -n 55aa | xxd -r -p | sudo dd of=/dev/$DEVICE bs=1 seek=510 count=2
;;
*)
echo "[-] Internal error 5" >&2
exit 1
;;
esac


###############################################################################
Expand All @@ -367,7 +525,7 @@ echo -n 55aa | xxd -r -p | sudo dd of=/dev/$DEVICE bs=1 seek=510 count=2
# following call to blkid sometimes exits with failure, even though the drive is formatted properly.
# `true` is so that a failure here doesn't cause entire script to exit prematurely
SUMMARY=$(sudo blkid -c /dev/null /dev/$DEVICE 2>/dev/null) || true
echo "[*] Successfully formatted $SUMMARY"
echo "[+] Successfully formatted $SUMMARY"

# TODO find a way to auto-mount (`sudo mount -a` doesn't work). in the meantime...
echo "Please disconnect/reconnect your drive now."

0 comments on commit 107cf1c

Please sign in to comment.