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 }