summaryrefslogtreecommitdiff
path: root/metastore.c
diff options
context:
space:
mode:
Diffstat (limited to 'metastore.c')
-rw-r--r--metastore.c539
1 files changed, 0 insertions, 539 deletions
diff --git a/metastore.c b/metastore.c
deleted file mode 100644
index 2956cab..0000000
--- a/metastore.c
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Main functions of the program.
- *
- * Copyright (C) 2007 David Härdeman <david@hardeman.nu>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#define _BSD_SOURCE
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <getopt.h>
-#include <utime.h>
-#include <sys/xattr.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "metastore.h"
-#include "settings.h"
-#include "utils.h"
-#include "metaentry.h"
-
-/* metastore settings */
-static struct metasettings settings = {
- .metafile = METAFILE,
- .do_mtime = false,
- .do_emptydirs = false,
- .do_removeemptydirs = false,
- .do_git = false,
-};
-
-/* Used to create lists of dirs / other files which are missing in the fs */
-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
- */
-static void
-insert_entry_plist(struct metaentry **list, struct metaentry *entry)
-{
- struct metaentry **parent;
-
- for (parent = list; *parent; parent = &((*parent)->list)) {
- if ((*parent)->pathlen > entry->pathlen)
- break;
- }
-
- entry->list = *parent;
- *parent = entry;
-}
-
-/*
- * Inserts an entry in a linked list ordered by pathlen descendingly
- */
-static void
-insert_entry_pdlist(struct metaentry **list, struct metaentry *entry)
-{
- struct metaentry **parent;
-
- for (parent = list; *parent; parent = &((*parent)->list)) {
- if ((*parent)->pathlen < entry->pathlen)
- break;
- }
-
- entry->list = *parent;
- *parent = entry;
-}
-
-/*
- * Prints differences between real and stored actual metadata
- * - for use in mentries_compare
- */
-static void
-compare_print(struct metaentry *real, struct metaentry *stored, int cmp)
-{
- if (!real && !stored) {
- msg(MSG_ERROR, "%s called with incorrect arguments\n", __FUNCTION__);
- return;
- }
-
- if (cmp == DIFF_NONE) {
- msg(MSG_DEBUG, "%s:\tno difference\n", real->path);
- return;
- }
-
- msg(MSG_QUIET, "%s:\t", real ? real->path : stored->path);
-
- if (cmp & DIFF_ADDED)
- msg(MSG_QUIET, "added ", real->path);
- if (cmp & DIFF_DELE)
- msg(MSG_QUIET, "removed ", stored->path);
- if (cmp & DIFF_OWNER)
- msg(MSG_QUIET, "owner ");
- if (cmp & DIFF_GROUP)
- msg(MSG_QUIET, "group ");
- if (cmp & DIFF_MODE)
- msg(MSG_QUIET, "mode ");
- if (cmp & DIFF_TYPE)
- msg(MSG_QUIET, "type ");
- if (cmp & DIFF_MTIME)
- msg(MSG_QUIET, "mtime ");
- if (cmp & DIFF_XATTR)
- msg(MSG_QUIET, "xattr ");
- msg(MSG_QUIET, "\n");
-}
-
-/*
- * Tries to change the real metadata to match the stored one
- * - for use in mentries_compare
- */
-static void
-compare_fix(struct metaentry *real, struct metaentry *stored, int cmp)
-{
- struct group *group;
- struct passwd *owner;
- gid_t gid = -1;
- uid_t uid = -1;
- struct utimbuf tbuf;
- unsigned i;
-
- if (!real && !stored) {
- msg(MSG_ERROR, "%s called with incorrect arguments\n",
- __FUNCTION__);
- return;
- }
-
- if (!real) {
- if (S_ISDIR(stored->mode))
- insert_entry_plist(&missingdirs, stored);
- else
- insert_entry_plist(&missingothers, stored);
-
- msg(MSG_NORMAL, "%s:\tremoved\n", stored->path);
- return;
- }
-
- if (!stored) {
- if (S_ISDIR(real->mode))
- insert_entry_pdlist(&extradirs, real);
- msg(MSG_NORMAL, "%s:\tadded\n", real->path);
- return;
- }
-
- if (cmp == DIFF_NONE) {
- msg(MSG_DEBUG, "%s:\tno difference\n", real->path);
- return;
- }
-
- if (cmp & DIFF_TYPE) {
- msg(MSG_NORMAL, "%s:\tnew type, will not change metadata\n",
- real->path);
- return;
- }
-
- msg(MSG_QUIET, "%s:\tchanging metadata\n", real->path);
-
- while (cmp & (DIFF_OWNER | DIFF_GROUP)) {
- if (cmp & DIFF_OWNER) {
- msg(MSG_NORMAL, "%s:\tchanging owner from %s to %s\n",
- real->path, real->group, stored->group);
- owner = xgetpwnam(stored->owner);
- if (!owner) {
- msg(MSG_DEBUG, "\tgetpwnam failed: %s\n",
- strerror(errno));
- break;
- }
- uid = owner->pw_uid;
- }
-
- if (cmp & DIFF_GROUP) {
- msg(MSG_NORMAL, "%s:\tchanging group from %s to %s\n",
- real->path, real->group, stored->group);
- group = xgetgrnam(stored->group);
- if (!group) {
- msg(MSG_DEBUG, "\tgetgrnam failed: %s\n",
- strerror(errno));
- break;
- }
- gid = group->gr_gid;
- }
-
- if (lchown(real->path, uid, gid)) {
- msg(MSG_DEBUG, "\tlchown failed: %s\n",
- strerror(errno));
- break;
- }
- break;
- }
-
- if (cmp & DIFF_MODE) {
- msg(MSG_NORMAL, "%s:\tchanging mode from 0%o to 0%o\n",
- real->path, real->mode & 07777, stored->mode & 07777);
- if (chmod(real->path, stored->mode & 07777))
- msg(MSG_DEBUG, "\tchmod failed: %s\n", strerror(errno));
- }
-
- /* FIXME: Use utimensat here, or even better - lutimensat */
- if ((cmp & DIFF_MTIME) && S_ISLNK(real->mode)) {
- msg(MSG_NORMAL, "%s:\tsymlink, not changing mtime\n", real->path);
- } else if (cmp & DIFF_MTIME) {
- msg(MSG_NORMAL, "%s:\tchanging mtime from %ld to %ld\n",
- real->path, real->mtime, stored->mtime);
- tbuf.actime = stored->mtime;
- tbuf.modtime = stored->mtime;
- if (utime(real->path, &tbuf)) {
- msg(MSG_DEBUG, "\tutime failed: %s\n", strerror(errno));
- return;
- }
- }
-
- if (cmp & DIFF_XATTR) {
- for (i = 0; i < real->xattrs; i++) {
- /* Any attrs to remove? */
- if (mentry_find_xattr(stored, real, i) >= 0)
- continue;
-
- msg(MSG_NORMAL, "%s:\tremoving xattr %s\n",
- real->path, real->xattr_names[i]);
- if (lremovexattr(real->path, real->xattr_names[i]))
- msg(MSG_DEBUG, "\tlremovexattr failed: %s\n",
- strerror(errno));
- }
-
- for (i = 0; i < stored->xattrs; i++) {
- /* Any xattrs to add? (on change they are removed above) */
- if (mentry_find_xattr(real, stored, i) >= 0)
- continue;
-
- msg(MSG_NORMAL, "%s:\tadding xattr %s\n",
- stored->path, stored->xattr_names[i]);
- if (lsetxattr(stored->path, stored->xattr_names[i],
- stored->xattr_values[i],
- stored->xattr_lvalues[i], XATTR_CREATE))
- msg(MSG_DEBUG, "\tlsetxattr failed: %s\n",
- strerror(errno));
- }
- }
-}
-
-/*
- * Tries to fix any empty dirs which are missing from the filesystem by
- * recreating them.
- */
-static void
-fixup_emptydirs(void)
-{
- struct metaentry *entry;
- struct metaentry *cur;
- struct metaentry **parent;
- char *bpath;
- char *delim;
- size_t blen;
- struct metaentry *new;
-
- if (!missingdirs)
- return;
- msg(MSG_DEBUG, "\nAttempting to recreate missing dirs\n");
-
- /* If directory x/y is missing, but file x/y/z is also missing,
- * we should prune directory x/y from the list of directories to
- * recreate since the deletition of x/y is likely to be genuine
- * (as opposed to empty dir pruning like git/cvs does).
- *
- * Also, if file x/y/z is missing, any child directories of
- * x/y should be pruned as they are probably also intentionally
- * removed.
- */
-
- msg(MSG_DEBUG, "List of candidate dirs:\n");
- for (cur = missingdirs; cur; cur = cur->list)
- msg(MSG_DEBUG, " %s\n", cur->path);
-
- for (entry = missingothers; entry; entry = entry->list) {
- msg(MSG_DEBUG, "Pruning using file %s\n", entry->path);
- bpath = xstrdup(entry->path);
- delim = strrchr(bpath, '/');
- if (!delim) {
- msg(MSG_NORMAL, "No delimiter found in %s\n", bpath);
- free(bpath);
- continue;
- }
- *delim = '\0';
-
- parent = &missingdirs;
- for (cur = *parent; cur; cur = cur->list) {
- if (strcmp(cur->path, bpath)) {
- parent = &cur->list;
- continue;
- }
-
- msg(MSG_DEBUG, "Prune phase 1 - %s\n", cur->path);
- *parent = cur->list;
- }
-
- /* Now also prune subdirs of the base dir */
- *delim++ = '/';
- *delim = '\0';
- blen = strlen(bpath);
-
- parent = &missingdirs;
- for (cur = *parent; cur; cur = cur->list) {
- if (strncmp(cur->path, bpath, blen)) {
- parent = &cur->list;
- continue;
- }
-
- msg(MSG_DEBUG, "Prune phase 2 - %s\n", cur->path);
- *parent = cur->list;
- }
-
- free(bpath);
- }
- msg(MSG_DEBUG, "\n");
-
- for (cur = missingdirs; cur; cur = cur->list) {
- msg(MSG_QUIET, "%s:\trecreating...", cur->path);
- if (mkdir(cur->path, cur->mode)) {
- msg(MSG_QUIET, "failed (%s)\n", strerror(errno));
- continue;
- }
- msg(MSG_QUIET, "ok\n");
-
- new = mentry_create(cur->path);
- if (!new) {
- msg(MSG_QUIET, "Failed to get metadata for %s\n");
- continue;
- }
-
- compare_fix(new, cur, mentry_compare(new, cur, &settings));
- }
-}
-
-/*
- * 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;) {
- msg(MSG_QUIET, "%s:\tremoving...", (*cur)->path);
- if (rmdir((*cur)->path)) {
- msg(MSG_QUIET, "failed (%s)\n", strerror(errno));
- cur = &(*cur)->list;
- continue;
- }
- /* No freeing, because OS will do the job at the end. */
- *cur = (*cur)->list;
- removed_dirs++;
- msg(MSG_QUIET, "ok\n");
- }
- }
-}
-
-/* Prints usage message and exits */
-static void
-usage(const char *arg0, const char *message)
-{
- if (message)
- msg(MSG_CRITICAL, "%s: %s\n\n", arg0, message);
- msg(MSG_CRITICAL,
-"Usage: %s ACTION [OPTION...] [PATH...]\n",
- arg0);
- msg(MSG_CRITICAL,
-"\n"
-"Where ACTION is one of:\n"
-" -c, --compare Show differences between stored and real metadata\n"
-" -s, --save Save current metadata\n"
-" -a, --apply Apply stored metadata\n"
-" -d, --dump Dump stored (if no PATH is given) or real metadata\n"
-" (if PATH is present, e.g. ./) in human-readable form\n"
-" -h, --help Help message (this text)\n"
-"\n"
-"Valid OPTIONS are:\n"
-" -v, --verbose Print more verbose messages\n"
-" -q, --quiet Print less verbose messages\n"
-" -m, --mtime Also take mtime into account for diff or apply\n"
-" -e, --empty-dirs Recreate missing empty directories\n"
-" -E, --remove-empty-dirs Remove extra empty directories\n"
-" -g, --git Do not omit .git directories\n"
-" -f, --file=FILE Set metadata file (" METAFILE " by default)\n"
- );
-
- exit(message ? EXIT_FAILURE : EXIT_SUCCESS);
-}
-
-/* Options */
-static struct option long_options[] = {
- { "compare", no_argument, NULL, 'c' },
- { "save", no_argument, NULL, 's' },
- { "apply", no_argument, NULL, 'a' },
- { "dump", no_argument, NULL, 'd' },
- { "help", no_argument, NULL, 'h' },
- { "verbose", no_argument, NULL, 'v' },
- { "quiet", no_argument, NULL, 'q' },
- { "mtime", no_argument, NULL, 'm' },
- { "empty-dirs", no_argument, NULL, 'e' },
- { "remove-empty-dirs", no_argument, NULL, 'E' },
- { "git", no_argument, NULL, 'g' },
- { "file", required_argument, NULL, 'f' },
- { NULL, 0, NULL, 0 }
-};
-
-/* Main function */
-int
-main(int argc, char **argv)
-{
- int i, c;
- struct metahash *real = NULL;
- struct metahash *stored = NULL;
- int action = 0;
-
- /* Parse options */
- i = 0;
- while (1) {
- int option_index = 0;
- c = getopt_long(argc, argv, "csadhvqmeEgf:",
- long_options, &option_index);
- if (c == -1)
- break;
- switch (c) {
- case 'c': /* compare */ action |= ACTION_DIFF; i++; break;
- case 's': /* save */ action |= ACTION_SAVE; i++; break;
- case 'a': /* apply */ action |= ACTION_APPLY; i++; break;
- case 'd': /* dump */ action |= ACTION_DUMP; i++; break;
- case 'h': /* help */ action |= ACTION_HELP; i++; break;
- case 'v': /* verbose */ adjust_verbosity(1); break;
- case 'q': /* quiet */ adjust_verbosity(-1); break;
- case 'm': /* mtime */ settings.do_mtime = true; break;
- case 'e': /* empty-dirs */ settings.do_emptydirs = true; break;
- case 'E': /* remove-empty-dirs */ settings.do_removeemptydirs = true;
- break;
- case 'g': /* git */ settings.do_git = true; break;
- case 'f': /* file */ settings.metafile = optarg; break;
- default:
- usage(argv[0], "unknown option");
- }
- }
-
- /* Make sure only one action is specified */
- if (i != 1)
- usage(argv[0], "incorrect option(s)");
-
- /* Make sure --empty-dirs is only used with apply */
- 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");
-
- if (action == ACTION_HELP)
- usage(argv[0], NULL);
-
- /* Perform action */
- if (action & ACTIONS_READING && !(action == ACTION_DUMP && optind < argc)) {
- mentries_fromfile(&stored, settings.metafile);
- if (!stored) {
- msg(MSG_CRITICAL, "Failed to load metadata from %s\n",
- settings.metafile);
- exit(EXIT_FAILURE);
- }
- }
-
- if (optind < argc) {
- while (optind < argc)
- mentries_recurse_path(argv[optind++], &real, &settings);
- } else if (action != ACTION_DUMP) {
- mentries_recurse_path(".", &real, &settings);
- }
-
- if (!real && (action != ACTION_DUMP || optind < argc)) {
- msg(MSG_CRITICAL,
- "Failed to load metadata from file system\n");
- exit(EXIT_FAILURE);
- }
-
- switch (action) {
- case ACTION_DIFF:
- mentries_compare(real, stored, compare_print, &settings);
- break;
- case ACTION_SAVE:
- mentries_tofile(real, settings.metafile);
- break;
- case ACTION_APPLY:
- mentries_compare(real, stored, compare_fix, &settings);
- if (settings.do_emptydirs)
- fixup_emptydirs();
- if (settings.do_removeemptydirs)
- fixup_newemptydirs();
- break;
- case ACTION_DUMP:
- mentries_dump(real ? real : stored);
- break;
- }
-
- exit(EXIT_SUCCESS);
-}
-