Here's my best effort at squashing this bug. It works fine for my series of backups and appears to produce the appropriate results. Still, caveat emptor. :)
A few comments about this patch: - Setting TMPNAME and calling deleteino() is not strictly necessary, and is merely done for optimization. - The two blocks in findunreflinks() could probably be merged -- I refrained to keep the patch less intrusive. - I couldn't think of insightful comments to include in the patch, especially after 5 or 6 tab stops. Sucks, though, as this is one of the best-commented programs I've seen. I tried my best to document the patch itself, so all would not be lost. -- There is no distinction between any AI program and some existent game.
>From 279c7540d6a060ffefedc95c8e3f1514b814134f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Bri=C3=A8re?= <fbri...@fbriere.net> Date: Sat, 20 Mar 2010 14:02:16 -0400 Subject: [PATCH] Properly deal with inodes excluded from incremental dump Files which are excluded from a dump (via -e or the nodump attribute) still have their inode marked as in-use in the dump header. This can cause problems when restoring from an incremental dump, if that inode is currently assigned in the symtable: - If assigned to a file that is being overwritten by another inode, the old entry will be renamed away, but never reclaimed, thus leaving the TMPNAME flag turned on and preventing its removal. - If assigned to a directory, that directory will not be removed by removeoldleaves(), and its entry will linger on in its parent's e_entries. - If its parent is being updated, removeleaf() will wrongly be called on the entry. The above comment about TMPNAME also applies. - If its parent is being deleted, this will fail due to the entry's presence. --- restore/restore.c | 27 ++++++++++++++++++++++++--- 1 files changed, 24 insertions(+), 3 deletions(-) diff --git a/restore/restore.c b/restore/restore.c index c87b532..537be33 100644 --- a/restore/restore.c +++ b/restore/restore.c @@ -566,6 +566,9 @@ keyval(int key) /* * Find unreferenced link names. + * + * This also takes care of directories which were missed by removeoldleaves(), + * because their inode has been reused, but excluded from the dump. */ void findunreflinks(void) @@ -583,12 +586,19 @@ findunreflinks(void) continue; for (j = 0; j < dirhash_size; j++) { for (np = ep->e_entries[j]; np != NULL; np = np->e_sibling) { - if (np->e_flags == 0) { + if ((np->e_flags & ~TMPNAME) == 0) { Dprintf(stdout, "%s: remove unreferenced name\n", myname(np)); - removeleaf(np); - freeentry(np); + if (np->e_type == LEAF) { + removeleaf(np); + freeentry(np); + } else { + np->e_flags |= TMPNAME; + deleteino(np->e_ino); + np->e_next = removelist; + removelist = np; + } } } } @@ -609,6 +619,17 @@ findunreflinks(void) myname(np)); removeleaf(np); freeentry(np); + } else { + if ((np->e_flags & ~TMPNAME) != 0) + badentry(np, "unreferenced with flags"); + + if (np->e_flags == 0) { + Dprintf(stdout, + "%s: remove unreferenced name\n", + myname(np)); + np->e_next = ep->e_next; + ep->e_next = np; + } } } } -- 1.7.0.2