From: Daniel Hokka Zakrisson Date: Sun, 22 Jul 2007 17:24:29 +0000 (+0000) Subject: Add exclude list support for vclone. X-Git-Tag: release-0.30.214~36 X-Git-Url: http://git.linux-vserver.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b6e9464ef52dfa0b7ae6c94ba1db94ece13df8b2;p=util-vserver.git Add exclude list support for vclone. git-svn-id: http://svn.linux-vserver.org/svn/util-vserver/trunk@2569 94cd875c-1c1d-0410-91d2-eb244daf1a30 --- diff --git a/lib_internal/Makefile-files b/lib_internal/Makefile-files index 215eca1..d21d4a8 100644 --- a/lib_internal/Makefile-files +++ b/lib_internal/Makefile-files @@ -101,6 +101,7 @@ lib_internal_libinternal_common_SRCS = \ lib_internal/util-isnumberunsigned.c \ lib_internal/util-lockfile.c \ lib_internal/util-safechdir.c \ + lib_internal/mkdir.c \ $(command_SRCS) \ $(filecfg_SRCS) diff --git a/lib_internal/matchlist-compare.c b/lib_internal/matchlist-compare.c index 2cff7e2..b46f7f6 100644 --- a/lib_internal/matchlist-compare.c +++ b/lib_internal/matchlist-compare.c @@ -37,5 +37,5 @@ MatchList_compare(struct MatchList const *list, char const *path) return ptr->type; } - return stINCLUDE; + return list->skip_depth > 0 ? stEXCLUDE : stINCLUDE; } diff --git a/lib_internal/mkdir.c b/lib_internal/mkdir.c new file mode 100644 index 0000000..717de0e --- /dev/null +++ b/lib_internal/mkdir.c @@ -0,0 +1,84 @@ +// $Id$ --*- c -*-- + +// Copyright (C) 2005 Enrico Scholz +// +// 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; version 2 of the License. +// +// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +static enum { mkdirFAIL, mkdirSUCCESS, mkdirSKIP } +mkdirSingle(char const *path, char *end_ptr, int good_err) +{ + *end_ptr = '\0'; + if (mkdir(path, 0700)!=-1 || errno==EEXIST) { + *end_ptr = '/'; + return mkdirSUCCESS; + } + else if (errno==good_err) { + *end_ptr = '/'; + return mkdirSKIP; + } + else + return mkdirFAIL; +} + +static char * +rstrchr(char *str, char c) +{ + while (*str!=c) --str; + return str; +} + +bool +mkdirRecursive(char const *path) +{ + char buf[strlen(path)+1]; + char * ptr = buf + sizeof(buf) - 2; + + if (path[0]!='/') return false; // only absolute paths + + strcpy(buf, path); + + while (ptr>buf && (ptr = rstrchr(ptr, '/'))!=0) { + switch (mkdirSingle(buf, ptr, ENOENT)) { + case mkdirSUCCESS : break; + case mkdirSKIP : --ptr; continue; + case mkdirFAIL : return false; + } + + break; // implied by mkdirSUCCESS + } + + assert(ptr!=0); + ++ptr; + + while ((ptr=strchr(ptr, '/'))!=0) { + switch (mkdirSingle(buf, ptr, 0)) { + case mkdirSKIP : + case mkdirFAIL : return false; + case mkdirSUCCESS : ++ptr; continue; + } + } + + return true; +} diff --git a/lib_internal/testsuite/Makefile-files b/lib_internal/testsuite/Makefile-files index 9b92856..314e512 100644 --- a/lib_internal/testsuite/Makefile-files +++ b/lib_internal/testsuite/Makefile-files @@ -23,17 +23,22 @@ check_PROGRAMS += lib_internal/testsuite/filecfg-ml \ lib_internal/testsuite/isnumber \ lib_internal/testsuite/isnumber-gnu \ lib_internal/testsuite/sigbus \ - lib_internal/testsuite/sigbus-gnu + lib_internal/testsuite/sigbus-gnu \ + lib_internal/testsuite/matchlist \ + lib_internal/testsuite/matchlist-gnu TESTS += lib_internal/testsuite/filecfg-ml \ lib_internal/testsuite/copy-check \ lib_internal/testsuite/isnumber \ lib_internal/testsuite/isnumber-gnu \ lib_internal/testsuite/sigbus \ - lib_internal/testsuite/sigbus-gnu + lib_internal/testsuite/sigbus-gnu \ + lib_internal/testsuite/matchlist \ + lib_internal/testsuite/matchlist-gnu endif DIETPROGS += lib_internal/testsuite/isnumber \ - lib_internal/testsuite/sigbus + lib_internal/testsuite/sigbus \ + lib_internal/testsuite/matchlist EXTRA_DIST += lib_internal/testsuite/copy-check @@ -66,3 +71,11 @@ lib_internal_testsuite_sigbus_CPPFLAGS = $(AM_CPPFLAGS) # see note above lib_internal_testsuite_sigbus_gnu_SOURCES = lib_internal/testsuite/sigbus.c lib_internal_testsuite_sigbus_gnu_CPPFLAGS = $(AM_CPPFLAGS) # see note above + +lib_internal_testsuite_matchlist_SOURCES = lib_internal/testsuite/matchlist.c +lib_internal_testsuite_matchlist_LDADD = $(LIBINTERNAL) +lib_internal_testsuite_matchlist_CPPFLAGS = $(AM_CPPFLAGS) # see note above + +lib_internal_testsuite_matchlist_gnu_SOURCES = lib_internal/testsuite/matchlist.c +lib_internal_testsuite_matchlist_gnu_LDADD = $(LIBINTERNAL_GLIBC) +lib_internal_testsuite_matchlist_gnu_CPPFLAGS = $(AM_CPPFLAGS) # see note above diff --git a/lib_internal/testsuite/matchlist.c b/lib_internal/testsuite/matchlist.c new file mode 100644 index 0000000..d469345 --- /dev/null +++ b/lib_internal/testsuite/matchlist.c @@ -0,0 +1,73 @@ +// $Id$ --*- c -*-- + +// Copyright (C) 2007 Daniel Hokka Zakrisson +// +// 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; version 2 of the License. +// +// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +int wrapper_exit_code = 255; + +int main(int argc, char *argv[]) +{ + struct MatchList list; + static const char *files[] = { + "/bin", + "+/bin/a", + }; + int test = 0; + uint32_t result = 0; + + MatchList_init(&list, "/", sizeof(files) / sizeof(*files)); + MatchList_appendFiles(&list, 0, files, sizeof(files) / sizeof(*files), true); + +#define DO_TEST(x) switch (MatchList_compare(&list, x)) { \ + case stINCLUDE: result |= 1 << test; break; \ + case stEXCLUDE: result |= 2 << test; break; \ + case stSKIP: result |= 4 << test; break; \ + } \ + test += 3; + DO_TEST("/bin"); + list.skip_depth++; + DO_TEST("/bin/a"); + DO_TEST("/bin/b"); + list.skip_depth--; + DO_TEST("/sbin"); + DO_TEST("/usr/lib/a"); + + MatchList_destroy(&list); + + if (result == 011212) + return 0; + else { + char buf[(sizeof(result) * 8) / 3 + 2], *ptr; + ssize_t i; + WRITE_MSG(1, "result = "); + buf[sizeof(buf) - 1] = '\0'; + for (i = 0, ptr = buf + sizeof(buf) - 2; i < (sizeof(result) * 8); i += 3, ptr--) + *ptr = '0' + ((result & (7 << i)) >> i); + WRITE_STR(1, buf); + WRITE_MSG(1, "\n"); + return 1; + } +} diff --git a/lib_internal/util.h b/lib_internal/util.h index 8a919d9..ad3a442 100644 --- a/lib_internal/util.h +++ b/lib_internal/util.h @@ -37,5 +37,6 @@ bool switchToWatchXid(char const **); size_t canonifyVserverName(char *); bool isNumber(char const *, signed long *result, bool is_strict); bool isNumberUnsigned(char const *, unsigned long *result, bool is_strict); +bool mkdirRecursive(char const *); #endif // H_UTILVSERVER_LIB_INTERNAL_UTIL_H diff --git a/src/vclone.c b/src/vclone.c index 2de812d..f1a8c0c 100644 --- a/src/vclone.c +++ b/src/vclone.c @@ -25,6 +25,7 @@ #include "lib_internal/pathinfo.h" #include "lib_internal/unify.h" +#include "lib_internal/matchlist.h" #include #include @@ -49,14 +50,16 @@ struct WalkdownInfo { - PathInfo state; - PathInfo src; - PathInfo dst; + PathInfo state; + PathInfo src; + PathInfo dst; + struct MatchList excludes; }; struct Arguments { unsigned int verbosity; xid_t xid; + const char * exclude_list; }; static struct WalkdownInfo global_info; @@ -66,9 +69,10 @@ int wrapper_exit_code = 1; struct option const CMDLINE_OPTIONS[] = { - { "help", no_argument, 0, CMD_HELP }, - { "version", no_argument, 0, CMD_VERSION }, - { "xid", required_argument, 0, CMD_XID }, + { "help", no_argument, 0, CMD_HELP }, + { "version", no_argument, 0, CMD_VERSION }, + { "xid", required_argument, 0, CMD_XID }, + { "exclude-from", required_argument, 0, 'X' }, { 0,0,0,0 } }; @@ -81,7 +85,8 @@ showHelp(int fd, char const *cmd, int res) WRITE_MSG(fd, "Usage:\n "); WRITE_STR(fd, cmd); WRITE_MSG(fd, - " [--xid ] \n\n" + " [--xid ] [--exclude-from ]\n" + " \n\n" "Please report bugs to " PACKAGE_BUGREPORT "\n"); exit(res); } @@ -107,6 +112,87 @@ bool Global_doRenew() { #include "vserver-visitdir.hc" +static bool +handleDirEntry(const PathInfo *src_path, const PathInfo *basename, + bool *is_dir, struct stat *st) +{ + bool res = false; + + *is_dir = false; + + if (lstat(basename->d, st)==-1) + PERROR_Q(ENSC_WRAPPERS_PREFIX "lstat", src_path->d); + else { + PathInfo dst_path = global_info.dst; + char dst_path_buf[ENSC_PI_APPSZ(dst_path, *src_path)]; + + if (S_ISDIR(st->st_mode)) + *is_dir = true; + + if (MatchList_compare(&global_info.excludes, src_path->d) != stINCLUDE) { + if (Global_getVerbosity() > 1) { + WRITE_MSG(1, " skipping '"); + Vwrite(1, src_path->d, src_path->l); + WRITE_MSG(1, "' (excluded)\n"); + } + return false; + } + + PathInfo_append(&dst_path, src_path, dst_path_buf); + + /* skip files that already exist */ + if (access(dst_path.d, F_OK)!=-1) { + if (Global_getVerbosity() > 1) { + WRITE_MSG(1, " skipping '"); + Vwrite(1, src_path->d, src_path->l); + WRITE_MSG(1, "' (exists in destination)\n"); + } + res = true; + } + else { + /* create directory that might have been skipped */ + if (global_info.excludes.skip_depth > 0) { + if (Global_getVerbosity() > 4) { + WRITE_MSG(1, " creating directories for '"); + Vwrite(1, dst_path.d, dst_path.l); + WRITE_MSG(1, "'\n"); + } + if (mkdirRecursive(dst_path.d) == -1) + PERROR_Q(ENSC_WRAPPERS_PREFIX "mkdirRecursive", dst_path.d); + } + + /* already unified file */ + if (S_ISREG(st->st_mode) && Unify_isIUnlinkable(basename->d) == unifyBUSY) { + if (Global_getVerbosity() > 2) { + WRITE_MSG(1, " linking unified file '"); + Vwrite(1, src_path->d, src_path->l); + WRITE_MSG(1, "'\n"); + } + Elink(basename->d, dst_path.d); + res = true; + } + /* something we have to copy */ + else { + if (Global_getVerbosity() > 2) { + WRITE_MSG(1, " copying non-unified file '"); + Vwrite(1, src_path->d, src_path->l); + WRITE_MSG(1, "'\n"); + } + if (!Unify_copy(basename->d, st, dst_path.d)) + PERROR_Q(ENSC_WRAPPERS_PREFIX "Unify_copy", dst_path.d); + else if (global_args->xid != VC_NOCTX && + vc_set_iattr(dst_path.d, global_args->xid, 0, VC_IATTR_XID) == -1) + PERROR_Q(ENSC_WRAPPERS_PREFIX "vc_set_iattr", dst_path.d); + else + res = true; + } + } + } + + return res; +} + +/* returns 1 on error, 0 on success */ static uint64_t visitDirEntry(struct dirent const *ent) { @@ -121,35 +207,19 @@ visitDirEntry(struct dirent const *ent) }; char path_buf[ENSC_PI_APPSZ(src_path, src_d_path)]; struct stat f_stat = { .st_dev = 0 }; + bool is_dir; PathInfo_append(&src_path, &src_d_path, path_buf); - - if (lstat(dirname, &f_stat)==-1) - perror(ENSC_WRAPPERS_PREFIX "lstat()"); - else { - PathInfo dst_path = global_info.dst; - char dst_path_buf[ENSC_PI_APPSZ(dst_path, src_path)]; - - PathInfo_append(&dst_path, &src_path, dst_path_buf); + if (handleDirEntry(&src_path, &src_d_path, &is_dir, &f_stat)) + res = 0; - /* skip files that already exist */ - if (access(dst_path.d, F_OK)!=-1) - res = 0; - else if (S_ISREG(f_stat.st_mode) && Unify_isIUnlinkable(src_d_path.d) == unifyBUSY) { - Elink(src_d_path.d, dst_path.d); - res = 0; - } - else { - if (!Unify_copy(src_d_path.d, &f_stat, dst_path.d)) - perror(ENSC_WRAPPERS_PREFIX "Unify_copy()"); - else if (vc_set_iattr(dst_path.d, global_args->xid, 0, VC_IATTR_XID) == -1) - perror(ENSC_WRAPPERS_PREFIX "vc_set_iattr()"); - else - res = 0; - } - if (S_ISDIR(f_stat.st_mode)) - res = visitDir(dirname, &f_stat); + if (is_dir) { + if (res || global_info.excludes.skip_depth > 0) + global_info.excludes.skip_depth++; + res = res + visitDir(dirname, &f_stat); + if (global_info.excludes.skip_depth > 0) + global_info.excludes.skip_depth--; } return res; @@ -160,13 +230,14 @@ int main(int argc, char *argv[]) struct Arguments args = { .verbosity = 0, .xid = VC_NOCTX, + .exclude_list = NULL, }; uint64_t res; int num_args; global_args = &args; while (1) { - int c = getopt_long(argc, argv, "+v", + int c = getopt_long(argc, argv, "+vX:", CMDLINE_OPTIONS, 0); if (c==-1) break; @@ -174,6 +245,7 @@ int main(int argc, char *argv[]) case CMD_HELP : showHelp(1, argv[0], 0); case CMD_VERSION : showVersion(); case 'v' : args.verbosity++; break; + case 'X' : args.exclude_list = optarg; break; case CMD_XID : args.xid = Evc_xidopt2xid(optarg,true); break; default : WRITE_MSG(2, "Try '"); @@ -212,11 +284,19 @@ int main(int argc, char *argv[]) ENSC_PI_SETSTR(global_info.src, argv[optind]); ENSC_PI_SETSTR(global_info.dst, argv[optind+1]); + if (global_args->exclude_list) + MatchList_initManually(&global_info.excludes, 0, strdup(argv[optind]), + global_args->exclude_list); + else + MatchList_init(&global_info.excludes, argv[optind], 0); + if (global_args->verbosity>3) WRITE_MSG(1, "Starting to traverse directories...\n"); Echdir(global_info.src.d); res = visitDir("/", 0); + + MatchList_destroy(&global_info.excludes); return res>0 ? 1 : 0; } diff --git a/src/vhashify.c b/src/vhashify.c index 1cfb875..b4edf3d 100644 --- a/src/vhashify.c +++ b/src/vhashify.c @@ -393,69 +393,6 @@ calculateHash(PathInfo const *filename, HashPath d_path, struct stat const * con return res; } -static enum { mkdirFAIL, mkdirSUCCESS, mkdirSKIP } -mkdirSingle(char const *path, char *end_ptr, int good_err) -{ - *end_ptr = '\0'; - if (mkdir(path, 0700)!=-1 || errno==EEXIST) { - *end_ptr = '/'; - return mkdirSUCCESS; - } - else if (errno==good_err) { - *end_ptr = '/'; - return mkdirSKIP; - } - else { - int old_errno = errno; - WRITE_MSG(2, "mkdir('"); - WRITE_STR(2, path); - errno = old_errno; - perror("')"); - return mkdirFAIL; - } -} - -static char * -rstrchr(char *str, char c) -{ - while (*str!=c) --str; - return str; -} - -static bool -mkdirRecursive(char const *path) -{ - if (path[0]!='/') return false; // only absolute paths - - char buf[strlen(path)+1]; - char * ptr = buf + sizeof(buf) - 2; - - strcpy(buf, path); - - while (ptr>buf && (ptr = rstrchr(ptr, '/'))!=0) { - switch (mkdirSingle(buf, ptr, ENOENT)) { - case mkdirSUCCESS : break; - case mkdirSKIP : --ptr; continue; - case mkdirFAIL : return false; - } - - break; // implied by mkdirSUCCESS - } - - assert(ptr!=0); - ++ptr; - - while ((ptr=strchr(ptr, '/'))!=0) { - switch (mkdirSingle(buf, ptr, 0)) { - case mkdirSKIP : - case mkdirFAIL : return false; - case mkdirSUCCESS : ++ptr; continue; - } - } - - return true; -} - static bool resolveCollisions(char *result, PathInfo const *root, HashPath d_path, struct stat *st, struct stat *hash_st) @@ -497,18 +434,16 @@ resolveCollisions(char *result, PathInfo const *root, HashPath d_path, if (!global_args->dry_run) { *ptr = '\0'; - if (!mkdirRecursive(result)) + if (!mkdirRecursive(result)) { + PERROR_Q("mkdir", result); return false; + } *ptr = '-'; int fd = open(result, O_NOFOLLOW|O_EXCL|O_CREAT|O_WRONLY, 0200); if (fd==-1) { - int old_errno = errno; - WRITE_MSG(2, "open('"); - WRITE_STR(2, buf); - errno = old_errno; - perror("')"); + PERROR_Q("open", buf); return false; }