Sunday, June 20, 2010

Bug with hdiutil and symlinks

I got an error report from an InstaDMG user who was using symlinks to point at their installer DVD. I had never tried out using symlinks for that, and so tried it out, successfully, and wrote back saying that it was working for me (with a much newer version of InstaDMG), and that they should probably be using the -I flag to specify the disk rather than use a symlink. But I did do some more testing while I was setup this way, and ran into a problem just once after a couple of runs of InstaDMG.

It turns out that there is a bug in hdiutil, at least on 10.6.4, when it comes to resolving symlinks. But this bug only seems to come out on some percentage of runs, and even then the percentage seems to vary with the hardware (or some other variable). On my iMac8,1 I see it 15-25% of the time, while with my iMac5,1 I only see it 0.5% of the time. Granted the older iMac is running a brand-new install, where the newer iMac is running an OS that I constantly beat on.

I have reported this back to Apple as Radar number 8111753, as well as on OpenRadar. But I am curious if other people are getting error numbers like I am, so if you would like to run the following script a few times on your system and post the results in the comments that would be great.

#!/bin/bash

# print the system information
/usr/sbin/system_profiler SPHardwareDataType SPSoftwareDataType | /usr/bin/awk '/Model Identifier:|System Version:/ { $1 = ""; $2 = ""; gsub(/^[ \t]+|[ \t]+$/,""); print }'

# create a temproary folder with three items in it
TEMP_FOLDER=`/usr/bin/mktemp -d /tmp/hdiutilBugTest.XXXX`
/usr/bin/touch "$TEMP_FOLDER/a"
/usr/bin/touch "$TEMP_FOLDER/b"
/usr/bin/touch "$TEMP_FOLDER/c"

# create a compressed image from the temp folder
/usr/bin/hdiutil create -srcfolder "$TEMP_FOLDER" "$TEMP_FOLDER/testImage.dmg" 1>/dev/null

# create the symlink to the image
/bin/ln -s "testImage.dmg" "$TEMP_FOLDER/symlink"

SYMLINK_PATH="$TEMP_FOLDER/symlink"
ABSOLUTE_PATH="$TEMP_FOLDER/testImage.dmg"

PATHS[0]="$SYMLINK_PATH"
PATHS[1]="$ABSOLUTE_PATH"

REPEAT_COUNT=1000
IFS=$'\n'
for THIS_PATH in ${PATHS[@]}; do
 echo "Working on: $THIS_PATH"
 FAILED_COUNT=0
 i=0
 while [ $i -lt $REPEAT_COUNT ]; do
  /usr/bin/hdiutil imageinfo "$THIS_PATH" 1>/dev/null 2>/dev/null
  if [ $? -ne 0 ]; then
   let FAILED_COUNT=FAILED_COUNT+1
  fi
  let i=i+1
 done
 echo "  Failed $FAILED_COUNT out of $REPEAT_COUNT times"
done

# delete the temp folder
if [ ! -z "$TEMP_FOLDER" ] && [ -d "$TEMP_FOLDER" ]; then
 /bin/rm -rf "$TEMP_FOLDER"
fi

Tuesday, June 8, 2010

html timer

For my presentation at Macworld in January I created a semi-time-lapse screen capture of a complete InstaDMG run to run as a demo. Since different parts of it were going to fly by at different rates I wanted to have some sort of timer to show the real clock time. Looking around for some little application or widget I did not find anything I like, and I finally gave in and made one myself.
Since I wanted this done fast, and with something I could easily control with AppleScript (sense the rest of the demo was being driven by it anyways), I decided to create a little JavaScript timer, and run it inside Safari.
<html>
<head>
    <title>Timer</title>
    <script>
        var hours = null, minutes = null, seconds = null
        var startTime = null
        var currentTimer = null
        
        function startTimer() {
            // setup things
            hours = document.getElementById("hours")
            minutes = document.getElementById("minutes")
            seconds = document.getElementById("seconds")
            
            startTime = new Date()
            displayTimer();
        }
        
        function displayTimer() {
            
            currentTime = new Date(new Date() - startTime)
            seconds.innerHTML = currentTime.getUTCSeconds()
            minutes.innerHTML = currentTime.getUTCMinutes()
            hours.innerHTML = currentTime.getUTCHours()
            currentTimer = setTimeout('displayTimer()',500);
        }
        
        function stopTimer() {
            clearTimeout(currentTimer)
        }
        
    </script>
    <style>
        body
{ font-size: large }
        div
{ width: .65in; display: inline-table; font-size: .5in; text-align: right }
    </style>

</head>
<body>
    <div id="hours">0</div> hrs <div id="minutes">0</div> min <div id="seconds">0</div> sec
</body>
</html>
Then I just had to trigger it with some code like:
tell application "Safari" to do JavaScript "startTimer()" in timerDocument
Edit: figured out the problem with the hours, and the correction was to use UTC time.