diff options
Diffstat (limited to 'metastore.c')
-rw-r--r-- | metastore.c | 72 |
1 files changed, 66 insertions, 6 deletions
diff --git a/metastore.c b/metastore.c index 323525a..257b003 100644 --- a/metastore.c +++ b/metastore.c @@ -38,6 +38,7 @@ static struct metasettings settings = { .metafile = METAFILE, .do_mtime = false, .do_emptydirs = false, + .do_removeemptydirs = false, .do_git = false, }; @@ -45,6 +46,9 @@ static struct metasettings settings = { static struct metaentry *missingdirs = NULL; static struct metaentry *missingothers = NULL; +/* Used to create lists of dirs / other files which are missing in metadata */ +static struct metaentry *extradirs = NULL; + /* * Inserts an entry in a linked list ordered by pathlen */ @@ -131,6 +135,8 @@ compare_fix(struct metaentry *real, struct metaentry *stored, int cmp) } if (!stored) { + if (S_ISDIR(real->mode)) + insert_entry_plist(&extradirs, real); msg(MSG_NORMAL, "%s:\tadded\n", real->path); return; } @@ -232,10 +238,8 @@ compare_fix(struct metaentry *real, struct metaentry *stored, int cmp) } /* - * Tries to fix any empty dirs which are missing by recreating them. - * An "empty" dir is one which either: - * - is empty; or - * - only contained empty dirs + * Tries to fix any empty dirs which are missing from the filesystem by + * recreating them. */ static void fixup_emptydirs(struct metahash *real, struct metahash *stored) @@ -326,6 +330,48 @@ fixup_emptydirs(struct metahash *real, struct metahash *stored) } } +/* + * Deletes any empty dirs present in the filesystem that are missing + * from the metadata. + * An "empty" dir is one which either: + * - is empty; or + * - only contains empty dirs + */ +static void +fixup_newemptydirs(void) +{ + struct metaentry *cur; + int removed_dirs = 1; + + if (!extradirs) + return; + + /* This is a simpleminded algorithm that attempts to rmdir() all + * directories discovered missing from the metadata. Naturally, this will + * succeed only on the truly empty directories, but depending on the order, + * it may mean that parent directory removal are attempted to be removed + * *before* the children. To circumvent this, keep looping around all the + * directories until none have been successfully removed. This is a + * O(N**2) algorithm, so don't try to remove too many nested directories + * at once (e.g. thousands). + * + * Note that this will succeed only if each parent directory is writable. + */ + while (removed_dirs) { + removed_dirs = 0; + msg(MSG_DEBUG, "\nAttempting to delete empty dirs\n"); + for (cur = extradirs; cur; cur = cur->list) { + msg(MSG_QUIET, "%s:\tremoving...", cur->path); + if (rmdir(cur->path)) { + msg(MSG_QUIET, "failed (%s)\n", strerror(errno)); + continue; + } + removed_dirs++; + msg(MSG_QUIET, "ok\n"); + } + } +} + /* Prints usage message and exits */ static void usage(const char *arg0, const char *message) @@ -342,7 +388,8 @@ usage(const char *arg0, const char *message) " -v, --verbose\t\tPrint more verbose messages\n" " -q, --quiet\t\tPrint less verbose messages\n" " -m, --mtime\t\tAlso take mtime into account for diff or apply\n" - " -e, --empty-dirs\tRecreate missing empty directories (experimental)\n" + " -E\t\t\tRemove extra empty directories\n" + " -e, --empty-dirs\tRecreate missing empty directories\n" " -g, --git\t\tDo not omit .git directories\n" " -f, --file <file>\tSet metadata file\n" ); @@ -360,6 +407,7 @@ static struct option long_options[] = { {"quiet", 0, 0, 0}, {"mtime", 0, 0, 0}, {"empty-dirs", 0, 0, 0}, + {"remove-empty-dirs", 0, 0, 0}, {"git", 0, 0, 0}, {"file", required_argument, 0, 0}, {0, 0, 0, 0} @@ -378,7 +426,7 @@ main(int argc, char **argv, char **envp) i = 0; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "csahvqmegf:", + c = getopt_long(argc, argv, "csahvqmeEgf:", long_options, &option_index); if (c == -1) break; @@ -396,6 +444,9 @@ main(int argc, char **argv, char **envp) } else if (!strcmp("empty-dirs", long_options[option_index].name)) { settings.do_emptydirs = true; + } else if (!strcmp("remove-empty-dirs", + long_options[option_index].name)) { + settings.do_removeemptydirs = true; } else if (!strcmp("git", long_options[option_index].name)) { settings.do_git = true; @@ -435,6 +486,9 @@ main(int argc, char **argv, char **envp) case 'e': settings.do_emptydirs = true; break; + case 'E': + settings.do_removeemptydirs = true; + break; case 'g': settings.do_git = true; break; @@ -454,6 +508,10 @@ main(int argc, char **argv, char **envp) if (settings.do_emptydirs && action != ACTION_APPLY) usage(argv[0], "--empty-dirs is only valid with --apply"); + /* Make sure --remove-empty-dirs is only used with apply */ + if (settings.do_removeemptydirs && action != ACTION_APPLY) + usage(argv[0], "--remove-empty-dirs is only valid with --apply"); + /* Perform action */ switch (action) { case ACTION_DIFF: @@ -522,6 +580,8 @@ main(int argc, char **argv, char **envp) if (settings.do_emptydirs) fixup_emptydirs(real, stored); + if (settings.do_removeemptydirs) + fixup_newemptydirs(); break; case ACTION_HELP: |