Monday, April 23, 2012

mtree on OS X

I came across a utility to test for changes in files (and more stuff, like permissions) in Chapter 4 of EdgeEnterprise Mac Security: mtree.

I'd never heard of it. There's a writeup about it here, as well as a couple of blog entries (here and here)

Rather than go straight to mtree (I still have to read the man page), this is a write-up of the bash script posted on the the first blog post. So it's really more about improving my bash scripting-fu (origin here) than it is about mtree.

> nl script.sh
1 #!/bin/sh

2 SCRIPT="`basename $0 | sed 's/\..*$//'`"

3 CONFIG=${1:-$HOME/.$SCRIPT}
4 DIR=${2:-$HOME/Documents}

5 cd $DIR || \
6        { echo "Failed to change working directory to $DIR" >&2; exit 1; }

7 if [ -f $CONFIG ]; then
8        /usr/sbin/mtree -f $CONFIG
9 fi

10 /usr/sbin/mtree -c -k time,md5digest,ripemd160digest > $CONFIG> 

I added line numbers with nl, you could use cat or other methods.

I hope line 1 is obvious (see wikipedia if it's not).

In line two, we take the variable $0, which is the path to the script, obtain its basename, and then strip off the extension:

> basename /Users/telliott/script.sh
script.sh
> echo x.txt | sed 's/\..*$//'
x

I had an even harder time with lines 3 and 4, but the folks on StackOverflow helped me.

I learned that bash has a man page (of course!). Also that one can search within a man page by entering "/ :-" after you get the page, which initiates a search for ":-". The answer:

${parameter:-word}
Use Default Values. If parameter is unset or null, the expansion
of word is substituted. Otherwise, the value of parameter is sub-
stituted.

So these two lines

3 CONFIG=${1:-$HOME/.$SCRIPT}
4 DIR=${2:-$HOME/Documents}

simply give default values for $CONFIG and $DIR if we don't supply them as arguments ($1 and $2) when invoking the script. If we called the script by ./script.sh from the Desktop, then we'd have $CONFIG be equal to ~/.script (a hidden file) and $DIR be equal to ~/Documents.

Lines 5 and 6 change the working directory to ~/Documents as default, if this fails (because the path input as an argument is no good) we write a message to >&2 and exit with 1 signifying failure. >&2 is stderr. This writes to the Terminal for me.

Lines 7 through 9 check whether $CONFIG exists already, if it does then we call mtree with the -f flag and $CONFIG argument:

/usr/sbin/mtree -f $CONFIG

From the mtree man page:

-f file
Read the specification from file, instead of from the standard
input.

If this option is specified twice, the two specifications are com-
pared with each other, rather than to the file hierarchy. The
specifications be sorted like output generated using -c. The out-
put format in this case is somewhat remniscent of comm(1), having
"in first spec only", "in second spec only", and "different" col-
umns, prefixed by zero, one and two TAB characters respectively.
Each entry in the "different" column occupies two lines, one from
each specification.

But the first time through, $CONFIG shouldn't exist yet. Instead, we call mtree with the -c and -k flags followed by various arguments, and write the result to $CONFIG. Let's do it in a simple way, as shown below. From the Desktop directory I input the command as shown, and write the results to x.txt in $HOME. As you can see, timestamps for all the Desktop files are there:

/usr/sbin/mtree -c -k time > x.txt
> cat ~/x.txt
#    user: telliott_admin
# machine: Toms-Mac-mini.local
#    tree: /Users/telliott_admin/Desktop
#    date: Mon Apr 23 17:20:21 2012

# .
/set type=file
.               type=dir time=1335216020.0
.DS_Store   time=1335216014.0
.localized  time=1333333575.0
notes.txt   time=1335210868.0
post.txt    time=1335216020.0
script.sh   time=1335215494.0
script.sh.txt \
time=1335211653.0
todo.txt    time=1335204803.0
..

>

Now, if I do it with -f, I get:

> /usr/sbin/mtree -f ~/x.txt
. changed
modification time expected Mon Apr 23 17:20:20 2012 found Mon Apr 23 17:20:51 2012
.DS_Store changed
modification time expected Mon Apr 23 17:20:14 2012 found Mon Apr 23 17:20:55 2012
post.txt changed
modification time expected Mon Apr 23 17:20:20 2012 found Mon Apr 23 17:20:51 2012

I see a change because I'm writing this text into post.txt! For some reason .DS_Store also changed. Next time I'll try to do something a little more useful.