Unexpected .DS_Store files?

I have occasionally encountered unexpected problems deleting directory trees (using rm -rf in a shell script). Recently, I encountered a similar problem using a third party application that failed to delete a directory tree using Java APIs. In both situations, the problem turned out to be a failure to remove a directory because it was not empty, and the file it contained was a .DS_Store file. This is odd for two reasons:

  1. The directory was not one I had viewed using Finder, so why was a .DS_Store file created?
  2. The .DS_store file was not protected, so why wasn't it deleted along with the other files in the directory?

The only explanation I can think of is that the .DS_Store file was created by a background thread, which is possible, but what would a background thread be doing that would cause a .DS_Store file to be created?

  • It is looking now that deleting a .DS_Store file is unreliable. This is what I did:

    find . -name .DS_Store -print ; find . -name .DS_Store -exec rm {} \; ; find . -name .DS_Store -print

    The first print statement listed 16 files. The second print statement listed 8 files. That means 8 files were deleted and 8 were not. Hmm... I repeated the deletion step and 4 of the 8 remaining files were deleted. It seems that every other file is deleted???

  • Unexplained .DS_Store files are still being created. I restarted, did a find, and found 80 new .DS_Store files in the tree that I have been looking at. Could mdsync or backupd do this?

  • Deleting a file (any file) may generate an FSEvent that triggers a background process to examine the directory or directory tree. If the background process somehow creates .DS_Store files, that would explain the behavior. Spotlight indexing and Time Machine backup come to mind as possible culprits.

Replies

The cluprit turns out to be Finder! Yes, if a directory is nested below a directory that is displayed by Finder with the Calculate All Sizes view option enabled, Finder will cache the directory cumulative size in a .DS_Store file in that directory and will update the cached size in response to FSEvents. The upshot: if a user uses the Calculate All Sizes option, standard Unix commands such as /bin/rm -rf may fail intermittently.

I've had some success using this script as a workaround:

#!/bin/sh

# echo First Pass
if [[ -d "$1" ]]; then
    # find "$1" -type l | wc -l 
    # find "$1" -type f | wc -l 
    # find "$1" -type d | wc -l
    # echo
    find "$1" -type l -name "*" -delete
    find "$1" -type f -name "*" -delete
fi

# echo Subsequent Passes 
while [ -d "$1" ]; do
    # find "$1" -type l | wc -l 
    # find "$1" -type f | wc -l 
    # find "$1" -type d | wc -l
    # echo
    find "$1" -type f -name "*" -delete
    find "$1" -type d -name "*" -delete
done

If you uncomment the lines that send output to standard output, set the Finder View Options to "Calculate all sizes", and run the script with a Finder window open to display a directory that is in the subtree that will be removed, I get output like this:

First Pass
       3
   13470
    1771

Subsequent Passes
       0
      19
    1771

The 19 files that show up in the subsequent pass are presumably ".DS_Store" files created by Finder because the first pass deletes all existing files.

The reason this script works is that the -delete option will fail to delete a directory (-type d) but it will not throw an error.

For directories, the -delete option also works up from the bottom of the tree so all 1771 directories can be deleted in a single pass if they contain each other but no other files.