KISS

Keep It Simple Stupid

A story of recovering files

| comments

One day I wanted to move a couple of files into the directory with the sources for this blog. It’s an often used directory, meaning fasd could help me with its path, but I used the wrong command, which resulted in this output and a bunch of missing files:

1
2
3
4
5
6
7
8
9
10
11
$ mv /Volumes/files/file{1,2} `fasd -d`
mv: rename 1 to /Users/user/Documents/var/1: No such file or directory
mv: rename 1.01117 to /Users/user/Documents/var/1.01117: No such file or directory
mv: rename 1.01117 to /Users/user/Documents/var/1.01117: No such file or directory
mv: rename /Volumes/disk/var/podcasts/backups/kazakh_v_kanade to /Users/user/Documents/var/kazakh_v_kanade: Permission denied
mv: rename 1.06288 to /Users/user/Documents/var/1.06288: No such file or directory
mv: rename 1.06288 to /Users/user/Documents/var/1.06288: No such file or directory
mv: rename 1.8225 to /Users/user/Documents/var/1.8225: No such file or directory
mv: rename 1.88823 to /Users/user/Documents/var/1.88823: No such file or directory
^C

This doesn’t look right at all. What was the output of that subcommand?

1
2
3
4
5
6
7
8
9
10
$ fasd -d
1.01117    /Volumes/disk/var/podcasts/backups/kazakh_v_kanade
1.13142    /Volumes/disk/var/podcasts/backups
1.18098    /Applications/Kindle.app
1.18098    /Volumes/disk/Backups.backupdb/mac/Latest
1.62       /usr/local
45.7268    /Users/user/src/octopress
59.2052    /Users/user/Documents/var
$ 1.01117 /Volumes/disk/var/podcasts/backups/kazakh_v_kanade 1.13142 /Volumes/disk/var/podcasts/backups 1.18098 /Applications/Kindle.app 1.18098 /Volumes/disk/Backups.backupdb/mac/Latest … 45.7268 /Users/user/src/octopress 59.2052 /Users/user/Documents/var

Oh my, that’s now what I expected; I thought it was going to present a picker. But…

The source files were gone in an unknown direction, and I noticed some odd behavior in CopyQ and Thunderbird.

(I should’ve read the output more thoroughly, then at least it would be quicker to find the new location of the files. But I also wouldn’t know what tmutil compare sucks; see the next section.)

TimeMachine

This was a seemingly simple task: figure out what changed in my filesystem since the last TimeMachine backup (which had been before these changes).

Oh look, man tmutil says there is this compare command to find the differences. Unfortunately I couldn’t get any meaningful information from it, i.e. for it to say that the directory ~/bin/ is in the latest backup, but no longer on disk. I tried a number of options, but not a single one of them worked as I expected:

1
2
3
4
5
$ tmutil compare -a
$ tmutil compare
$ tmutil compare -tsmu
$ tmutil compare -tsmuU
$ tmutil compare -sU /Volumes/disk/Backups.backupdb/mac/Latest

The output of a few of them looked like this:

1
2
3
4
5
6
7
8
9
10
- 504.4M                        /Volumes/disk/Backups.backupdb/mac/2021-11-18-200000/Recovery
- 0B                            /Volumes/disk/Backups.backupdb/mac/2021-11-18-200000/MacintoshHD-Data/Users
+ 523.1G                        /System/Volumes/Data

-------------------------------------
Added:         523.1G
Removed:       28.7G
Changed:       0B

Some of them returned more lines, but nothing useful for /Users:

1
2
3
4
- 0B                            /Volumes/disk/Backups.backupdb/mac/2021-11-18-200000/MacintoshHD-Data/Users
+ 0B                            /System/Volumes/Data/Users

Then I tried comparing the Data volume directly, but:

1
2
3
4
5
6
7
$ tmutil compare -sU /Volumes/disk/Backups.backupdb/mac/Latest/MacintoshHD-Data /System/Volumes/Data
- 0B                            /Volumes/disk/Backups.backupdb/mac/2021-11-18-200000/MacintoshHD-Data/Users/.localized
- 6.2M                          /Volumes/disk/Backups.backupdb/mac/2021-11-18-200000/MacintoshHD-Data/Users/Shared
- 0B                            /Volumes/disk/Backups.backupdb/mac/2021-11-18-200000/MacintoshHD-Data/Users/test
- 326.2G                        /Volumes/disk/Backups.backupdb/mac/2021-11-18-200000/MacintoshHD-Data/Users/user

“326.2G” removed?! This is a complete and utter nonsense! I have no idea what this output means.

DiffMerge

Well, to find missing files, I can compare the directories recursively. I started with DiffMerge, but it would probably take a while to find the differences since it also compared the file contents. So to the next option.

Comparing directories

In the end I went with the manual approach in the terminal, which means it’s very composable and customizable to whatever I need. And that is find . | sort — it can print the sorted paths to all the files in all subdirectories recursively. Basically I needed to compare the file list of /Volumes/disk/Backups.backupdb/mac/Latest/MacHD-Data/Users/user/ and $HOME/.

1
2
$ ( cd ~; find . | sort > ~/home_now; )
$ ( cd /Volumes/disk/Backups.backupdb/mac/Latest/MacintoshHD-Data/Users/user/; find . | sort > ~/home_backup; )

The files were 230–320 MiB, but vimdiff was fine comparing them. It showed me exactly what directories (and files inside) were missing. Easy!

After looking at the changes, I realized that about two dozens of directories were only moved into one ~/Documents/var/ (not removed), so at least I found the place.

I had to compare $HOME, /Applications and /usr/local to manually restore all those items to their places. It took some time, but I’m sure everything is correct now.

1
2
3
4
$ mv bin ~
$ mv CopyQ.app /Applications
$ mv public ~/src/octopress/
# etc.

A number of discoveries

TM and symlinks

Apparently tmutil restore can’t restore symlinks (at least on OS X 10.15.7):

1
2
3
4
$ tmutil restore -v /Volumes/disk/Backups.backupdb/mac/Latest/MacintoshHD-Data/Users/user/docs ~/
/Users/user/Documents: Source path is not inside a snapshot.
$ ll /Volumes/disk/Backups.backupdb/mac/Latest/MacintoshHD-Data/Users/user/docs
lrwxr-xr-x+ 1 u  staff  21 Jan  1  2020 /Volumes/disk/Backups.backupdb/mac/Latest/MacintoshHD-Data/Users/user/docs -> /Users/user/Documents

Yes, I know that ~/docs is a symlink for ~/Documents and I want TM to restore the symlink itself, but it can’t?!

Auto-excluded directories

I didn’t know Xcode automatically excluded build directories from TM backups:

1
2
3
4
5
6
7
8
$ xattr -l ~/src/xcplugin/XVim2/build
com.apple.XcodeGenerated: Yes
com.apple.metadata:com_apple_backup_excludeItem:
00000000  62 70 6C 69 73 74 30 30 5F 10 11 63 6F 6D 2E 61  |bplist00_..com.a|
00000010  70 70 6C 65 2E 62 61 63 6B 75 70 64 08 00 00 00  |pple.backupd....|
00000020  00 00 00 01 01 00 00 00 00 00 00 00 01 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00 00 00 00 00 1C           |.............|
0000003d

Evidently rust does the same:

1
2
3
4
5
6
7
$ xattr -l ~/src/wireguard-vanity-address/target
com.apple.metadata:com_apple_backup_excludeItem:
00000000  62 70 6C 69 73 74 30 30 5F 10 11 63 6F 6D 2E 61  |bplist00_..com.a|
00000010  70 70 6C 65 2E 62 61 63 6B 75 70 64 08 00 00 00  |pple.backupd....|
00000020  00 00 00 01 01 00 00 00 00 00 00 00 01 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00 00 00 00 00 1C           |.............|
0000003d

Non-standard path in Alfred

I mentioned in the beginning that CopyQ had troubles saving the clipboard history right after this wrong move, so I restarted it and it was fine. However only when I found the application here instead of /Applications/, I discovered that Alfred launched the program from this location and I hadn’t noticed that before (since Alfred’s purpose is launch programs fast).

fasd

Finally, what I wanted to do initially was this:

1
2
3
$ mv /Volumes/files/file{1,2} `fasd -d octo`
# or with the default alias:
$ mv /Volumes/files/file{1,2} `d octo`

where:

1
2
3
4
5
$ fasd -d octo
1.19575    /Users/u/.rvm/gems/ruby-2.6.0@octo
42.892     /Users/u/src/octopress
$ echo "$(fasd -d octo)"
/Users/u/src/octopress

Comments