Add exclude list support for vclone.
authorDaniel Hokka Zakrisson <daniel@hozac.com>
Sun, 22 Jul 2007 17:24:29 +0000 (17:24 +0000)
committerDaniel Hokka Zakrisson <daniel@hozac.com>
Sun, 22 Jul 2007 17:24:29 +0000 (17:24 +0000)
git-svn-id: http://svn.linux-vserver.org/svn/util-vserver/trunk@2569 94cd875c-1c1d-0410-91d2-eb244daf1a30

lib_internal/Makefile-files
lib_internal/matchlist-compare.c
lib_internal/mkdir.c [new file with mode: 0644]
lib_internal/testsuite/Makefile-files
lib_internal/testsuite/matchlist.c [new file with mode: 0644]
lib_internal/util.h
src/vclone.c
src/vhashify.c

index 215eca1..d21d4a8 100644 (file)
@@ -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)
 
index 2cff7e2..b46f7f6 100644 (file)
@@ -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 (file)
index 0000000..717de0e
--- /dev/null
@@ -0,0 +1,84 @@
+// $Id$    --*- c -*--
+
+// Copyright (C) 2005 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+//  
+// 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 <config.h>
+#endif
+
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+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;
+}
index 9b92856..314e512 100644 (file)
@@ -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 (file)
index 0000000..d469345
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <lib_internal/util.h>
+#include <lib_internal/matchlist.h>
+
+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;
+       }
+}
index 8a919d9..ad3a442 100644 (file)
@@ -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
index 2de812d..f1a8c0c 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "lib_internal/pathinfo.h"
 #include "lib_internal/unify.h"
+#include "lib_internal/matchlist.h"
 
 #include <unistd.h>
 #include <getopt.h>
 
 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 <xid>] <source> <absolute path to destination>\n\n"
+           " [--xid <xid>] [--exclude-from <exclude-list>]\n"
+           "         <source> <absolute path to destination>\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;
 }
index 1cfb875..b4edf3d 100644 (file)
@@ -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;
       }