diff options
author | Przemyslaw Pawelczyk <przemoc@gmail.com> | 2015-09-13 22:12:14 +0200 |
---|---|---|
committer | Przemyslaw Pawelczyk <przemoc@gmail.com> | 2015-09-13 22:12:14 +0200 |
commit | 16ab153f3a54194e3217fcf1a235904e4b61623b (patch) | |
tree | c27f9326815ebf4454e753028dcb3d388b3c0990 /metastore.c | |
parent | 295428a550bb55a509ce15c5e27a0ab173799ebb (diff) |
Move source files to src/ directory.
As a bonus you can build out-of-tree now via make -f.
It's part of the work related to issue #22.
Diffstat (limited to 'metastore.c')
-rw-r--r-- | metastore.c | 539 |
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); -} - |