vshost-util-vserver

Build script and sources for util-vserver.
git clone https://ccx.te2000.cz/git/vshost-util-vserver
Log | Files | Refs

vclone.c (7912B)


      1 // $Id$    --*- c -*--
      2 
      3 // Copyright (C) 2007 Daniel Hokka Zakrisson
      4 //  
      5 // This program is free software; you can redistribute it and/or modify
      6 // it under the terms of the GNU General Public License as published by
      7 // the Free Software Foundation; version 2 of the License.
      8 //  
      9 // This program is distributed in the hope that it will be useful,
     10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 // GNU General Public License for more details.
     13 //  
     14 // You should have received a copy of the GNU General Public License
     15 // along with this program; if not, write to the Free Software
     16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     17 
     18 
     19 #ifdef HAVE_CONFIG_H
     20 #  include <config.h>
     21 #endif
     22 
     23 #include "util.h"
     24 #include "vserver.h"
     25 
     26 #include "lib_internal/pathinfo.h"
     27 #include "lib_internal/unify.h"
     28 #include "lib_internal/matchlist.h"
     29 
     30 #include <unistd.h>
     31 #include <getopt.h>
     32 #include <fcntl.h>
     33 #include <dirent.h>
     34 #include <errno.h>
     35 #include <assert.h>
     36 #include <utime.h>
     37 #include <libgen.h>
     38 #include <sys/param.h>
     39 
     40 #define ENSC_WRAPPERS_PREFIX	"vclone: "
     41 #define ENSC_WRAPPERS_UNISTD	1
     42 #define ENSC_WRAPPERS_FCNTL	1
     43 #define ENSC_WRAPPERS_DIRENT	1
     44 #define ENSC_WRAPPERS_VSERVER	1
     45 #include <wrappers.h>
     46 
     47 #define CMD_HELP		0x8000
     48 #define CMD_VERSION		0x8001
     49 #define CMD_XID			0x8002
     50 
     51 struct WalkdownInfo
     52 {
     53     PathInfo		state;
     54     PathInfo		src;
     55     PathInfo		dst;
     56     struct MatchList	excludes;
     57 };
     58 
     59 struct Arguments {
     60     unsigned int	verbosity;
     61     xid_t		xid;
     62     const char *	exclude_list;
     63 };
     64 
     65 static struct WalkdownInfo		global_info;
     66 static struct Arguments const *		global_args;
     67 
     68 int wrapper_exit_code = 1;
     69 
     70 struct option const
     71 CMDLINE_OPTIONS[] = {
     72   { "help",         no_argument,       0, CMD_HELP },
     73   { "version",      no_argument,       0, CMD_VERSION },
     74   { "xid",          required_argument, 0, CMD_XID },
     75   { "exclude-from", required_argument, 0, 'X' },
     76   { 0,0,0,0 }
     77 };
     78 
     79 
     80 static void
     81 showHelp(int fd, char const *cmd, int res)
     82 {
     83   VSERVER_DECLARE_CMD(cmd);
     84   
     85   WRITE_MSG(fd, "Usage:\n  ");
     86   WRITE_STR(fd, cmd);
     87   WRITE_MSG(fd,
     88 	    " [--xid <xid>] [--exclude-from <exclude-list>]\n"
     89 	    "         <source> <absolute path to destination>\n\n"
     90 	    "Please report bugs to " PACKAGE_BUGREPORT "\n");
     91   exit(res);
     92 }
     93 
     94 static void
     95 showVersion()
     96 {
     97   WRITE_MSG(1,
     98 	    "vclone " VERSION " -- clones a guest\n"
     99 	    "This program is part of " PACKAGE_STRING "\n\n"
    100 	    "Copyright (C) 2007 Daniel Hokka Zakrisson\n"
    101 	    VERSION_COPYRIGHT_DISCLAIMER);
    102   exit(0);
    103 }
    104 
    105 int Global_getVerbosity() {
    106   return global_args->verbosity;
    107 }
    108 
    109 bool Global_doRenew() {
    110   return true;
    111 }
    112 
    113 #include "vserver-visitdir.hc"
    114 
    115 static bool
    116 handleDirEntry(const PathInfo *src_path, const PathInfo *basename,
    117 	      bool *is_dir, struct stat *st)
    118 {
    119   bool res = false;
    120 
    121   *is_dir = false;
    122 
    123   if (lstat(basename->d, st)==-1)
    124     PERROR_Q(ENSC_WRAPPERS_PREFIX "lstat", src_path->d);
    125   else {
    126     PathInfo		dst_path = global_info.dst;
    127     char		dst_path_buf[ENSC_PI_APPSZ(dst_path, *src_path)];
    128     struct stat		dst_st;
    129 
    130     if (S_ISDIR(st->st_mode))
    131       *is_dir = true;
    132 
    133     if (MatchList_compare(&global_info.excludes, src_path->d) != stINCLUDE) {
    134       if (Global_getVerbosity() > 1) {
    135 	WRITE_MSG(1, "  skipping '");
    136 	Vwrite(1, src_path->d, src_path->l);
    137 	WRITE_MSG(1, "' (excluded)\n");
    138       }
    139       return true;
    140     }
    141 
    142     PathInfo_append(&dst_path, src_path, dst_path_buf);
    143 
    144     /* skip files that already exist */
    145     if (lstat(dst_path.d, &dst_st)!=-1) {
    146       if (Global_getVerbosity() > 1) {
    147 	WRITE_MSG(1, "  skipping '");
    148 	Vwrite(1, src_path->d, src_path->l);
    149 	WRITE_MSG(1, "' (exists in destination)\n");
    150       }
    151       res = true;
    152     }
    153     else {
    154       /* create directory that might have been skipped */
    155       if (global_info.excludes.skip_depth > 0) {
    156 	if (Global_getVerbosity() > 4) {
    157 	  WRITE_MSG(1, "  creating directories for '");
    158 	  Vwrite(1, dst_path.d, dst_path.l);
    159 	  WRITE_MSG(1, "'\n");
    160 	}
    161 	if (mkdirRecursive(dst_path.d) == -1)
    162 	  PERROR_Q(ENSC_WRAPPERS_PREFIX "mkdirRecursive", dst_path.d);
    163       }
    164 
    165       /* already unified file */
    166       if (S_ISREG(st->st_mode) && Unify_isIUnlinkable(basename->d) == unifyBUSY) {
    167 	if (Global_getVerbosity() > 2) {
    168 	  WRITE_MSG(1, "  linking unified file '");
    169 	  Vwrite(1, src_path->d, src_path->l);
    170 	  WRITE_MSG(1, "'\n");
    171 	}
    172 	Elink(basename->d, dst_path.d);
    173 	res = true;
    174       }
    175       /* something we have to copy */
    176       else {
    177 	if (Global_getVerbosity() > 2) {
    178 	  WRITE_MSG(1, "  copying non-unified file '");
    179 	  Vwrite(1, src_path->d, src_path->l);
    180 	  WRITE_MSG(1, "'\n");
    181 	}
    182 	if (!Unify_copy(basename->d, st, dst_path.d))
    183 	  PERROR_Q(ENSC_WRAPPERS_PREFIX "Unify_copy", dst_path.d);
    184 	else if (!S_ISSOCK(st->st_mode) &&
    185 		 global_args->xid != VC_NOCTX &&
    186 		 vc_set_iattr(dst_path.d, global_args->xid, 0, VC_IATTR_XID) == -1 &&
    187 		 errno != EINVAL)
    188 	  PERROR_Q(ENSC_WRAPPERS_PREFIX "vc_set_iattr", dst_path.d);
    189 	else
    190 	  res = true;
    191       }
    192     }
    193   }
    194 
    195   return res;
    196 }
    197 
    198 /* returns 1 on error, 0 on success */
    199 static uint64_t
    200 visitDirEntry(struct dirent const *ent)
    201 {
    202   char const *			dirname  = ent->d_name;
    203   if (isDotfile(dirname)) return 0;
    204 
    205   uint64_t			res      = 1;
    206   PathInfo			src_path = global_info.state;
    207   PathInfo			src_d_path = {
    208     .d = dirname,
    209     .l = strlen(dirname)
    210   };
    211   char				path_buf[ENSC_PI_APPSZ(src_path, src_d_path)];
    212   struct stat			f_stat = { .st_dev = 0 };
    213   bool				is_dir;
    214 
    215   PathInfo_append(&src_path, &src_d_path, path_buf);
    216 
    217   if (handleDirEntry(&src_path, &src_d_path, &is_dir, &f_stat))
    218     res = 0;
    219 
    220   if (is_dir) {
    221     if (res || global_info.excludes.skip_depth > 0)
    222       global_info.excludes.skip_depth++;
    223     res = res + visitDir(dirname, &f_stat);
    224     if (global_info.excludes.skip_depth > 0)
    225       global_info.excludes.skip_depth--;
    226   }
    227 
    228   return res;
    229 }
    230 
    231 int main(int argc, char *argv[])
    232 {
    233   struct Arguments	args = {
    234     .verbosity		=  0,
    235     .xid		= VC_NOCTX,
    236     .exclude_list	= NULL,
    237   };
    238   uint64_t		res;
    239   int			num_args;
    240 
    241   global_args = &args;
    242   while (1) {
    243     int		c = getopt_long(argc, argv, "+vX:",
    244 				CMDLINE_OPTIONS, 0);
    245     if (c==-1) break;
    246 
    247     switch (c) {
    248       case CMD_HELP	:  showHelp(1, argv[0], 0);
    249       case CMD_VERSION	:  showVersion();
    250       case 'v'		:  args.verbosity++; break;
    251       case 'X'		:  args.exclude_list = optarg; break;
    252       case CMD_XID	:  args.xid = Evc_xidopt2xid(optarg,true); break;
    253       default		:
    254 	WRITE_MSG(2, "Try '");
    255 	WRITE_STR(2, argv[0]);
    256 	WRITE_MSG(2, " --help' for more information.\n");
    257 	return EXIT_FAILURE;
    258 	break;
    259     }
    260   }
    261 
    262   num_args = argc - optind;
    263   if (num_args < 1) {
    264     WRITE_MSG(2, "Source is missing; try '");
    265     WRITE_STR(2, argv[0]);
    266     WRITE_MSG(2, " --help' for more information.\n");
    267     return EXIT_FAILURE;
    268   }
    269   else if (num_args < 2) {
    270     WRITE_MSG(2, "Destination is missing; try '");
    271     WRITE_STR(2, argv[0]);
    272     WRITE_MSG(2, " --help' for more information.\n");
    273     return EXIT_FAILURE;
    274   }
    275   else if (num_args > 2) {
    276     WRITE_MSG(2, "Too many arguments; try '");
    277     WRITE_STR(2, argv[0]);
    278     WRITE_MSG(2, " --help' for more information.\n");
    279     return EXIT_FAILURE;
    280   }
    281   else if (*argv[optind+1] != '/') {
    282     WRITE_MSG(2, "The destination must be an absolute path; try '");
    283     WRITE_STR(2, argv[0]);
    284     WRITE_MSG(2, " --help' for more information.\n");
    285     return EXIT_FAILURE;
    286   }
    287   ENSC_PI_SETSTR(global_info.src, argv[optind]);
    288   ENSC_PI_SETSTR(global_info.dst, argv[optind+1]);
    289 
    290   if (global_args->exclude_list)
    291     MatchList_initManually(&global_info.excludes, 0, strdup(argv[optind]),
    292 			   global_args->exclude_list);
    293   else
    294     MatchList_init(&global_info.excludes, argv[optind], 0);
    295 
    296   if (global_args->verbosity>3)
    297     WRITE_MSG(1, "Starting to traverse directories...\n");
    298 
    299   Echdir(global_info.src.d);
    300   res = visitDir("/", 0);
    301 
    302   MatchList_destroy(&global_info.excludes);
    303   
    304   return res>0 ? 1 : 0;
    305 }