How to auto mount internal SATA hard disk drives after power failure/interruption

automounthard drive

In a Mac OS X 10.7.5 (Lion) system with two internal SATA drives, the internal drives are connected via a Highpoint Rocketraid controller and are configured without any RAID thus operating in legacy mode.

Both locally attached SATA hard disk drives are not mounted after the Mac is powered up after a power failure/interruption (unclean shutdown). On the next clean reboot the two local volumes are mounted.

When the drive/volume has no read/write activity, then the volume fails to mount. In case there is no I/O activity on the drive/volume then the drive/volume will mount.

Both local volumes are identical:

$ diskutil info disk2s1
   Device Identifier:        disk2s1
   Device Node:              /dev/disk2s1
   Part of Whole:            disk2
   Device / Media Name:      Untitled 1

   Volume Name:              A
   Escaped with Unicode:     A

   Mounted:                  Yes
   Mount Point:              /Volumes/A
   Escaped with Unicode:     /Volumes/A

   File System Personality:  HFS+
   Type (Bundle):            hfs
   Name (User Visible):      Mac OS Extended
   Owners:                   Enabled

   Partition Type:           Apple_HFS
   OS Can Be Installed:      No
   Media Type:               Generic
   Protocol:                 SCSI
   SMART Status:             Not Supported
   Volume UUID:              45D248ED-29E3-3344-8B62-4E43499568CE

   Total Size:               3.0 TB (3000592979456 Bytes) (exactly 5860533163 512-Byte-Blocks)
   Volume Free Space:        3.0 TB (2997719433216 Bytes) (exactly 5854920768 512-Byte-Blocks)
   Device Block Size:        512 Bytes

   Read-Only Media:          No
   Read-Only Volume:         No
   Ejectable:                Yes

   Whole:                    No
   Internal:                 No

Partial log:

Jul 12 10:32:48 nl3 kernel[0]: jnl: disk3s2: replay_journal: from: 4367872 to: 5375488 (joffset 0x1c3000)
Jul 12 10:32:48 nl3 kernel[0]: jnl: disk3s2: journal replay done.
Jul 12 10:32:49 nl3 kernel[0]: hfs: Removed 0 orphaned / unlinked files and 329 directories 
Jul 12 15:26:15 nl3 kernel[0]: disk3s2: device/channel is not attached.
Jul 12 15:26:15 nl3 kernel[0]: /Volumes/Time Machine] [FSLogMsgID 381179371] [FSLogMsgOrder Last] 
Jul 12 15:26:15 nl3 kernel[0]: disk3s2: device/channel is not attached.
Jul 12 15:26:15: --- last message repeated 10 times ---
Jul 12 15:26:15 nl3 kernel[0]: jnl: disk3s2: do_jnl_io: strategy err 0x6
Jul 12 15:26:15 nl3 kernel[0]: jnl: disk3s2: write_journal_header: error writing the journal header!
Jul 12 15:26:15 nl3 kernel[0]: disk3s2: device/channel is not attached.
Jul 12 15:26:15: --- last message repeated 48 times ---
Jul 12 15:26:15 nl3 kernel[0]: 2309.inProgress/.Backup.363792189.590451.log] [FSLogMsgID 662186116] [FSLogMsgOrder Last] 
Jul 12 15:26:15 nl3 kernel[0]: disk3s2: device/channel is not attached.
Jul 12 15:26:16: --- last message repeated 76 times ---
Jul 12 15:26:16 nl3 kernel[0]: 0451.log] [FSLogMsgID 860380055] [FSLogMsgOrder Last] 
Jul 12 15:26:16 nl3 kernel[0]: disk3s2: device/channel is not attached.
Jul 12 15:26:20: --- last message repeated 56 times ---
Jul 12 15:26:20 nl3 kernel[0]: disk3s2: media is not present.
Jul 12 15:26:20: --- last message repeated 7 times ---
Jul 12 15:26:20 nl3 kernel[0]: jnl: disk3s2: close: journal 0x602ed30, is invalid.  aborting outstanding transactions
Jul 12 15:29:00 nl3 kernel[0]: jnl: disk3s2: replay_journal: from: 4588544 to: 4118528 (joffset 0x1d9000)
Jul 12 15:29:01 nl3 kernel[0]: jnl: disk3s2: journal replay done.
Jul 12 15:47:16 nl3 kernel[0]: jnl: disk3s2: do_jnl_io: strategy err 0xdisk3s2: device/channel is not attached.
Jul 12 15:47:16 nl3 kernel[0]: 6
Jul 12 15:47:16 nl3 kernel[0]: jnl: disk3s2: write_journal_header: error writing the journal header!
Jul 12 15:47:16 nl3 kernel[0]: disk3s2: device/channel is not attached.
Jul 12 15:47:24: --- last message repeated 15 times ---
Jul 12 15:47:24 nl3 kernel[0]: disk3s2: media is not present.
Jul 12 15:47:24: --- last message repeated 18 times ---
Jul 12 15:47:24 nl3 kernel[0]: jnl: disk3s2: close: journal 0x602ed30, is invalid.  aborting outstanding transactions

/dev/rdisk3s2: fsck_hfs run at Thu Jul 12 15:55:59 2012
/dev/rdisk3s2: ** /dev/rdisk3s2 (NO WRITE)
/dev/rdisk3s2:    Executing fsck_hfs (version diskdev_cmds-540.1~25).
QUICKCHECK ONLY; FILESYSTEM DIRTY

/dev/rdisk3s2: fsck_hfs run at Thu Jul 12 15:55:59 2012
/dev/rdisk3s2: ** /dev/rdisk3s2
/dev/rdisk3s2:    Executing fsck_hfs (version diskdev_cmds-540.1~25).

/dev/rdisk3s1: fsck_hfs run at Thu Jul 12 16:01:55 2012
/dev/rdisk3s1: ** /dev/rdisk3s1 (NO WRITE)
/dev/rdisk3s1:    Executing fsck_hfs (version diskdev_cmds-540.1~25).
QUICKCHECK ONLY; FILESYSTEM CLEAN

And both drives have an identical GUID partitioning schema and HFS+ file system:

$ diskutil list
/dev/disk0
   #:                       TYPE NAME                    SIZE       IDENTIFIER
...
/dev/disk1
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *3.0 TB     disk1
   1:                  Apple_HFS J                       3.0 TB     disk1s1
/dev/disk2
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *3.0 TB     disk2
   1:                  Apple_HFS A                       3.0 TB     disk2s1

How to automatically mount these hard drives automatically after an unclean shutdown?

Best Answer

Boot script

The best thing I can come up with, is a script that will run at boot, and will check whether each Apple_HFS TYPE of volume (disk*s*) is mounted. When the volume isn't mounted, try to repair and mount the volume.

Script commands explained

List local HFS volumes

The diskutil list command is used to retrieve all local volumes that contain an Apple HFS file system. For example:

$ diskutil list | grep ":                  Apple_HFS" | awk '{ print $NF }'
disk0s2
disk1s1
disk2s1

Is volume mounted?

The df -lnh command is used to check whether that volume is mounted. When the volume is not mounted, the command returns no (empty) output. And when the volume is mounted, the output is like:

$ df -lnh | grep /dev/disk1s1
/dev/disk1s1   3.6Ti   13Mi  3.6Ti     1%    /Volumes/L

Step by step installation

Create bash script (version #2)

$ sudo nano /Library/Scripts/BootRepairMount.sh

Paste this text into the editor and save it.

#!/bin/bash
TRIES=0
MAXTRIES=60
until diskutil list > /dev/null;do
    TRIES=$(($TRIES+1))
    if [ $TRIES -gt $MAXTRIES ]; then
        exit $?
    fi
    sleep 1
done
TRIES=0
MAXTRIES=3
for OUTPUT in $(diskutil list | grep ':                  Apple_HFS' | awk '{ print $NF }')
do
    if [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then
        echo "$OUTPUT is not mounted, repair and mount"
        until diskutil repairVolume $OUTPUT; do
            TRIES=$(($TRIES+1))
            if [ $TRIES -gt $MAXTRIES ]; then
                break
            else
                sleep 1
            fi
        done
        TRIES=0
        until diskutil mount $OUTPUT; do
            TRIES=$(($TRIES+1))
            if [ $TRIES -gt $MAXTRIES ]; then
                break
            else
                sleep 1
            fi
        done
        TRIES=0
    fi
done

Set the permissions of the script for root access only

$ sudo chown -R root:admin /Library/Scripts/BootRepairMount.sh

Make the script readable and executable for all users

$ sudo chmod a=rx /Library/Scripts/BootRepairMount.sh

Make the script admin writeable and executable

$ sudo chmod u=rwx /Library/Scripts/BootRepairMount.sh

Test run the script

$ /Library/Scripts/BootRepairMount.sh

Create a .plist file for launchd to run on boot

$ sudo nano /Library/LaunchDaemons/nl.probackup.bootrepairmount.plist

And paste this text into the editor window.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>nl.probackup.bootrepairmount</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Scripts/BootRepairMount.sh</string>
</array>
<key>UserName</key>
<string>root</string>
<key>UserGroup</key>
<string>wheel</string>
<key>RunAtLoad</key>
<true></true>
<key>Debug</key>
<true></true>
</dict>
</plist>

Change launchd plist permissions

$ sudo chown -R root:wheel /Library/LaunchDaemons/nl.probackup.bootrepairmount.plist

Load the plist into launchd

$ sudo launchctl load /Library/LaunchDaemons/nl.probackup.bootrepairmount.plist

Confirm that the plist is loaded

$ sudo launchctl list | grep .bootrepairmount

If the plist name appears, like:

851 -   nl.probackup.bootrepairmount

it’s installed.

Stop running script on boot

To remove the .plist, in other words stop the script from being run at boot, type the following:

$ sudo launchctl unload -w /Library/LaunchDaemons/nl.probackup.bootrepairmount.plist

All in one line - copy and paste - installation

file=/Library/Scripts/BootRepairMount.sh;{ echo \#\!/bin/bash; echo TRIES=0; echo MAXTRIES=60; echo until\ diskutil\ list\ \>\ /dev/null\;do; echo $'\t'TRIES=\$\(\(\$TRIES+1\)\); echo $'\t'if\ \[\ \$TRIES\ -gt\ \$MAXTRIES\ \]\;\ then; echo $'\t'$'\t'exit\ \$\?; echo $'\t'fi; echo $'\t'sleep\ 1; echo done; echo TRIES=0; echo MAXTRIES=3; echo for\ OUTPUT\ in\ \$\(diskutil\ list\ \|\ grep\ \':\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Apple_HFS\'\ \|\ awk\ \'\{\ print\ \$NF\ \}\'\); echo do; echo $'\t'if\ \[\[\ -z\ \$\(df\ -lnh\ \|\ grep\ /dev/\$OUTPUT\)\ \]\]\;\ then; echo $'\t'$'\t'echo\ \"\$OUTPUT\ is\ not\ mounted,\ repair\ and\ mount\"; echo $'\t'$'\t'until\ diskutil\ repairVolume\ \$OUTPUT\;\ do; echo $'\t'$'\t'$'\t'TRIES=\$\(\(\$TRIES+1\)\); echo $'\t'$'\t'$'\t'if\ \[\ \$TRIES\ -gt\ \$MAXTRIES\ \]\;\ then; echo $'\t'$'\t'$'\t'$'\t'break; echo $'\t'$'\t'$'\t'else; echo $'\t'$'\t'$'\t'$'\t'sleep\ 1; echo $'\t'$'\t'$'\t'fi; echo $'\t'$'\t'done; echo $'\t'$'\t'TRIES=0; echo $'\t'$'\t'until\ diskutil\ mount\ \$OUTPUT\;\ do; echo $'\t'$'\t'$'\t'TRIES=\$\(\(\$TRIES+1\)\); echo $'\t'$'\t'$'\t'if\ \[\ \$TRIES\ -gt\ \$MAXTRIES\ \]\;\ then; echo $'\t'$'\t'$'\t'$'\t'break; echo $'\t'$'\t'$'\t'else; echo $'\t'$'\t'$'\t'$'\t'sleep\ 1; echo $'\t'$'\t'$'\t'fi; echo $'\t'$'\t'done; echo $'\t'$'\t'TRIES=0; echo $'\t'fi; echo done; } > ~/out_file;cat ~/out_file | sudo tee $file;rm ~/out_file;sudo chown -R root:admin $file;ls -l $file;sudo chmod a=rx $file;sudo chmod u=rwx /$file;file=/Library/LaunchDaemons/nl.probackup.bootrepairmount.plist;sudo launchctl unload -w $file &>/dev/null;{ echo \<\?xml\ version=\"1.0\"\ encoding=\"UTF-8\"\?\>; echo \<\!DOCTYPE\ plist\ PUBLIC\ \"-//Apple//DTD\ PLIST\ 1.0//EN\"\ \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\>; echo \<plist\ version=\"1.0\"\>; echo \<dict\>; echo \<key\>Label\</key\>; echo \<string\>nl.probackup.bootrepairmount\</string\>; echo \<key\>ProgramArguments\</key\>; echo \<array\>; echo \<string\>/Library/Scripts/BootRepairMount.sh\</string\>; echo \</array\>; echo \<key\>UserName\</key\>; echo \<string\>root\</string\>; echo \<key\>UserGroup\</key\>; echo \<string\>wheel\</string\>; echo \<key\>RunAtLoad\</key\>; echo \<true\>\</true\>; echo \<key\>Debug\</key\>; echo \<true\>\</true\>; echo \</dict\>; echo \</plist\>; } > ~/out_file;cat ~/out_file | sudo tee /$file;rm ~/out_file;sudo chown -R root:wheel $file;sudo launchctl load -w -F $file;sudo launchctl list | grep .bootrepairmount