Advertisement
MestreLion

ntfsuuid - Tools for manipulating UUIDs in NTFS filesystems

Jul 27th, 2011
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 10.14 KB | None | 0 0
  1. #!/bin/bash
  2. #
  3. # ntfsuuid - Tools for manipulating UUIDs in NTFS filesystems
  4. #
  5. #    Copyright (C) 2011 Rodrigo Silva - <[email protected]>
  6. #
  7. #    This program is free software: you can redistribute it and/or modify
  8. #    it under the terms of the GNU General Public License as published by
  9. #    the Free Software Foundation, either version 3 of the License, or
  10. #    (at your option) any later version.
  11. #
  12. #    This program is distributed in the hope that it will be useful,
  13. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. #    GNU General Public License for more details.
  16. #
  17. #    You should have received a copy of the GNU General Public License
  18. #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. # Huge thanks for all the gurus in irc://irc.freenet.org/#bash
  21. # and the contributors of http://mywiki.wooledge.org/
  22. #
  23. # TODO: Integrate with common/common lib (SELF, _Confirm, _TestRoot)
  24. # TODO: Test avaliability of tools (xxd,blkid)
  25. # TODO: Document error messages
  26. # TODO: Allow volume LABEL instead of /dev/sdxy
  27. # TODO: reconsider getopts http://mywiki.wooledge.org/BashFAQ/035
  28. # TODO: reconsider UPPER CASE variables (UUID conflict)
  29. # TODO: consider dropping the lame, redundant "return $?" (==return)
  30. # FIXME: Revert byte order in UUID
  31.  
  32. exec 3> /dev/null # to avoid harcoding /dev/null everywhere. For tools' stderr.
  33.  
  34. SELF="${0##*/}" # builin $(basename $0)
  35.  
  36. COMMAND=
  37. VERBOSE=
  38. LOWER=
  39. FORCE=
  40. TEST=
  41. DEBUG=
  42. BINARY=
  43. DEVICE=
  44. UUID=
  45.  
  46. usage ()
  47. {
  48.     local explicit="$1"
  49.    
  50.     cat <<EOF
  51. Usage: $SELF [OPTIONS] --generate
  52.        $SELF [OPTIONS] --read     DEVICE
  53.        $SELF [OPTIONS] --set      DEVICE [--uuid UUID] [--binary]
  54.        $SELF [OPTIONS] --copy     INPUT_DEVICE OUTPUT_DEVICE
  55. EOF
  56.     if [[ -z "$explicit" ]] ; then
  57.         cat <<EOF
  58. Try '$SELF --help' for more information.
  59. EOF
  60.     else
  61.         cat <<EOF
  62.  
  63. Tools for reading, setting and generating UUIDs suitable for NTFS filesystems (8 bytes)
  64.  
  65. Options:
  66.   -h, --help            print this message and exit
  67.   -v, --verbose         verbose output (displays extra information)
  68.   -l, --lower           use lower case for displaying UUIDs
  69.   -f, --force           do not prompt before changes, assumes Yes for all questions
  70.   -t, --test            simulation test, do not write any data on disk (affects --set and --copy only)
  71.   -d, --debug           displays debug information on parsed options and arguments
  72.   -b, --binary          interprets UUID as 8-byte binary data (rather than hexadecimal string)
  73.                         --binary is only used with --uuid, otherwise it is ignored
  74.   -u, --uuid UUID       uses the specified UUID. Must be a 16 chars ASCII string, case-insensitive, [0-9,A-F]
  75.                         if --binary is used, UUID must be 8 bytes long
  76.                         --uuid is only used with --set, otherwise it is ignored
  77.  
  78. Commands:
  79.   --generate            output a random UUID suitable for an NTFS device (8 bytes)
  80.   --read DEVICE         output the current UUID of the specified NTFS device
  81.   --set  DEVICE         sets the UUID for the specified NTFS device (uses a random one if --uuid is ommited)
  82.   --copy DEV_IN DEV_OUT copy (clone) the UUID from an NTFS device to another
  83.  
  84. Examples:
  85.   $SELF --generate
  86.   $SELF --read /dev/sdx1
  87.   $SELF --set  /dev/sdx1
  88.   $SELF --set  /dev/sdx1 --uuid 2AE2C85D31835048
  89.   $SELF --set  /dev/sdx1 --uuid rodrigo1 --binary
  90.   $SELF --copy /dev/sdx1 /dev/sdx2
  91.  
  92. Author: Rodrigo Silva (a.k.a. MestreLion) <[email protected]>
  93. License: GPLv3 <http://www.gnu.org/licenses/>
  94. EOF
  95.     fi
  96. }
  97.  
  98. #######################################  Auxiliary Functions
  99.  
  100. _RandomUuid()
  101. {
  102.     local lower=""
  103.    
  104.     if [[ -z "$LOWER" ]] ; then lower="-u" ; fi
  105.     UUID=$(xxd "$lower" -plain -l 8 /dev/urandom)
  106. }
  107.  
  108. _TestNTFS()
  109. {
  110.     local device="$1"
  111.    
  112.     if [[ -n "$device" ]] ; then
  113.         if [[ -b "$device" ]] ; then
  114.             if sudo blkid -p  "$device" | grep -q "TYPE=\"ntfs\"" ; then
  115.                 return 0
  116.             else
  117.                 echo "error: $device: permission denied or not an NTFS device"
  118.                 return 13
  119.             fi
  120.         else
  121.             echo "error: $device: not found or not a device"
  122.             return 12
  123.         fi
  124.     else
  125.         # Redundant, since DEVICE is mandatory, enforced by getopts
  126.         echo "error: this operation requires a device"
  127.         return 11
  128.     fi
  129. }
  130.  
  131. _Confirm()
  132. {
  133.     local message="$1"
  134.     local confirm
  135.    
  136.     if [[ -z "$FORCE" ]] ; then
  137.         # can be improved with smarter setting of default value
  138.         read -p "$message (y/n, default YES): " confirm
  139.         case "x$confirm" in
  140.             x[Yy]*|x) ;;
  141.             *) if [[ -n "$VERBOSE" ]] ; then echo "$SELF: $COMMAND: cancelled by user" ; fi ; return 2 ;;
  142.         esac
  143.     fi
  144.    
  145.     return 0
  146. }
  147.  
  148. _PrintFlags()
  149. {
  150.     # Echoing $DEBUG value is redundant, since this will only be printed if = 1
  151.     cat <<EOF
  152. Parsed Options  =$GETOPT
  153. Debug           = $DEBUG
  154. Verbose         = $VERBOSE
  155. Lower case      = $LOWER
  156. Non-Interactive = $FORCE
  157. Simulation Test = $TEST
  158. Binary          = $BINARY
  159. Command         = $COMMAND
  160. Device          = $DEVICE
  161. Uuid            = $UUID
  162. EOF
  163. }
  164.  
  165.  
  166. #######################################  Main Functions
  167.  
  168.  
  169. Uuid_Generate()
  170. {
  171.     _RandomUuid
  172.    
  173.     # All input data is set, print debug info
  174.     if [[ -n "$DEBUG" ]] ; then _PrintFlags ; fi
  175.  
  176.     if [[ -n "$VERBOSE" ]] ; then
  177.         echo "Random UUID: $UUID"
  178.     else
  179.         echo "$UUID"
  180.     fi
  181.    
  182.     return 0
  183. }
  184.  
  185. Uuid_Read()
  186. {
  187.     local lower=""
  188.    
  189.     _TestNTFS "$DEVICE" || return $?
  190.  
  191.     if [[ -z "$LOWER" ]] ; then lower="-u" ; fi
  192.     UUID=$(sudo xxd "$lower" -plain -s 72 -len 8 "$DEVICE")
  193.  
  194.     # All input data is set, print debug info
  195.     if [[ -n "$DEBUG" ]] ; then _PrintFlags ; fi
  196.  
  197.     if [[ -n "$VERBOSE" ]] ; then
  198.         echo "UUID of $DEVICE is $UUID"
  199.     else
  200.         echo $UUID
  201.     fi
  202.    
  203.     return 0
  204. }
  205.  
  206. Uuid_Set()
  207. {
  208.     local binary_uuid
  209.     local result=0
  210.    
  211.     # Handle UUID: Calculate a random one if not given
  212.     if [[ -z "$UUID" ]] ; then
  213.         _RandomUuid
  214.     else
  215.         if [[ -z "$BINARY" ]] ; then       
  216.             # Hexadecimal String format
  217.             if ! [[ "$UUID" =~ ^[0-9A-Fa-f]{16}$ ]] ; then
  218.                 echo "error: $UUID: invalid UUID. Must be 16 hexadecimal characters [0-9,A-F]. Ex: 2AE2C85D31835048"
  219.                 return 21
  220.             fi
  221.         else
  222.             # Binary format
  223.             if [[ ${#UUID} -ne 8 ]] ; then
  224.                 echo "error: $UUID: invalid UUID. Binary format must be 8-bytes long"
  225.                 return 22
  226.             fi
  227.            
  228.             # Save original UUID
  229.             binary_uuid="$UUID"
  230.            
  231.             # Convert to hexadecimal string
  232.             UUID=$(echo "$UUID" | xxd -len 8 -plain)
  233.          fi
  234.     fi
  235.    
  236.     # Adjust UUID case
  237.     if [[ -n "$LOWER" ]] ; then UUID="${UUID,,}" ; else UUID="${UUID^^}" ; fi
  238.  
  239.     # All input data is set, print debug info
  240.     if [[ -n "$DEBUG" ]] ; then _PrintFlags ; fi
  241.     if [[ -n "$binary_uuid" ]] ; then echo "Binary UUID     = $binary_uuid" ; fi
  242.    
  243.     # Test device
  244.     _TestNTFS "$DEVICE" || return $?
  245.  
  246.     # Confirm with user
  247.     _Confirm "Set $DEVICE UUID to $UUID?" || return $?
  248.    
  249.     # Set the UUID
  250.     if [[ -z "$TEST" ]] ; then
  251.         echo "$UUID" | xxd -r -plain | sudo dd of="$DEVICE" bs=8 count=1 seek=9 \
  252.                                                conv=notrunc status=noxfer 2>&3
  253.         result=$?
  254.     fi
  255.  
  256.     if [[ $result -eq 0 ]] ; then
  257.         # Verbose message
  258.         if [ -n "$VERBOSE" ] ; then echo "UUID of $DEVICE set to $UUID" ; fi
  259.         return 0
  260.     else
  261.         echo "error: $COMMAND: $result (permission denied?)"
  262.         return $result
  263.     fi
  264. }
  265.  
  266. Uuid_Copy()
  267. {
  268.     local input="$DEVICE"
  269.     local output="$1"
  270.     local result=0
  271.     local lower
  272.    
  273.     # Check DEVICE_OUTPUT (getopt only handle 1 mandatory argument per option)
  274.     if [[ -z "$output" ]] ; then
  275.         echo "error: output device not specified"
  276.         usage
  277.         exit 31
  278.     fi
  279.    
  280.     # Test Input and Output devices
  281.     _TestNTFS "$input" && _TestNTFS "$output" || return $?
  282.  
  283.     # Read input device's UUID
  284.     if [[ -z "$LOWER" ]] ; then lower="-u" ; fi
  285.     UUID=$(sudo xxd "$lower" -plain -s 72 -len 8 "$input")
  286.  
  287.     # All input data is set, print debug info (with additional DEVICE_OUTPUT parameter)
  288.     if [[ -n "$DEBUG" ]] ; then _PrintFlags ; echo "Output          = $1" ; fi
  289.  
  290.     # Confirm with user
  291.     _Confirm "Copy UUID $UUID from $input to $output ?" || return $?
  292.            
  293.     # Copy the UUID
  294.     if [[ -z "$TEST" ]] ; then
  295.         sudo dd if="$input" of="$output" bs=8 count=1 seek=9 skip=9 conv=notrunc /
  296.                 status=noxfer 2>&3
  297.         result=$?
  298.     fi
  299.  
  300.     if [[ $result -eq 0 ]] ; then
  301.         # Verbose message
  302.         if [[ -n "$VERBOSE" ]] ; then echo "UUID $UUID copied from $input to $output" ; fi
  303.         return 0
  304.     else
  305.         echo "error: $COMMAND: $result (permission denied?)"
  306.         return $result
  307.     fi
  308. }
  309.  
  310. #######################################  Command Line parsing
  311.  
  312.  
  313. # Print usage if no parameter passed
  314. if [[ $# -eq 0 ]] ; then
  315.     usage
  316.     exit 1
  317. fi
  318.  
  319. GETOPT=$(getopt --alternative --options vlftbd --longoptions verbose,lower,force,test,binary,debug,uuid:,help,generate,read:,set:,copy: --name "$SELF" -- "$@")
  320. if [[ $? -gt 0 ]] ; then usage ; exit 1 ; fi # error in getopt
  321. eval set -- "$GETOPT"
  322.  
  323. while true ; do
  324.     case "$1" in
  325.         -v|--verbose ) shift ; VERBOSE="1"                          ;; # Set verbose
  326.         -l|--lower   ) shift ; LOWER="1"                            ;; # Set lower case
  327.         -f|--force   ) shift ; FORCE="1"                            ;; # Set non-interactive
  328.         -t|--test    ) shift ; TEST="1"                             ;; # Set simulation test
  329.         -b|--binary  ) shift ; BINARY="1"                           ;; # Set binary mode
  330.         -d|--debug   ) shift ; DEBUG="1"                            ;; # Set debug mode
  331.         --uuid       ) shift ; UUID="$1"                    ; shift ;; # Set UUID
  332.         --help       ) shift ; COMMAND="help"                       ;; # Help requested
  333.         --generate   ) shift ; COMMAND="generate"                   ;; # Prints random UUID
  334.         --read       ) shift ; COMMAND="read" ; DEVICE="$1" ; shift ;; # Reads UUID from dev
  335.         --set        ) shift ; COMMAND="set"  ; DEVICE="$1" ; shift ;; # Sets UUID of dev
  336.         --copy       ) shift ; COMMAND="copy" ; DEVICE="$1" ; shift ;; # Copy UUID from dev
  337.         --           ) shift ; break ;;
  338.         *            ) break ;;
  339.     esac
  340. done
  341.  
  342. case "$COMMAND" in
  343.     help     ) usage "HELP"   ; exit 0  ;;
  344.     generate ) Uuid_Generate  ; exit $? ;;
  345.     read     ) Uuid_Read      ; exit $? ;;
  346.     set      ) Uuid_Set       ; exit $? ;;
  347.     copy     ) Uuid_Copy "$@" ; exit $? ;;
  348.     *        ) usage          ; exit 1  ;;
  349. esac
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement