vcopy.c (8558B)
1 // $Id$ --*- c -*-- 2 3 // Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de> 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/matchlist.h" 27 #include "lib_internal/unify.h" 28 29 #include <unistd.h> 30 #include <getopt.h> 31 #include <fcntl.h> 32 #include <dirent.h> 33 #include <errno.h> 34 #include <assert.h> 35 #include <utime.h> 36 #include <libgen.h> 37 #include <sys/param.h> 38 39 #define ENSC_WRAPPERS_UNISTD 1 40 #define ENSC_WRAPPERS_FCNTL 1 41 #define ENSC_WRAPPERS_DIRENT 1 42 #include <wrappers.h> 43 44 #define CMD_HELP 0x8000 45 #define CMD_VERSION 0x8001 46 #define CMD_MANUALLY 0x8002 47 #define CMD_STRICT 0x8003 48 49 struct WalkdownInfo 50 { 51 PathInfo state; 52 struct MatchList dst_list; 53 struct MatchList src_list; 54 }; 55 56 struct Arguments { 57 enum {mdMANUALLY, mdVSERVER} mode; 58 bool do_dry_run; 59 unsigned int verbosity; 60 bool local_fs; 61 bool is_strict; 62 }; 63 64 static struct WalkdownInfo global_info; 65 static struct Arguments const * global_args; 66 67 int wrapper_exit_code = 1; 68 69 struct option const 70 CMDLINE_OPTIONS[] = { 71 { "help", no_argument, 0, CMD_HELP }, 72 { "version", no_argument, 0, CMD_VERSION }, 73 { "manually", no_argument, 0, CMD_MANUALLY }, 74 { "strict", no_argument, 0, CMD_STRICT }, 75 { 0,0,0,0 } 76 }; 77 78 typedef enum { opUNIFY, opCOPY, opDIR, opSKIP } Operation; 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 " [-nv] [--strict] <dst-vserver> <src-vserver>\n or\n "); 89 WRITE_STR(fd, cmd); 90 WRITE_MSG(fd, 91 " --manually [-nvx] [--] <dst-path> <dst-excludelist> <src-path> <src-excludelist>\n\n" 92 " --manually ... unify generic paths; excludelists must be generated\n" 93 " manually\n" 94 " --strict ... require an existing vserver configuration for dst-vserver;\n" 95 " by default, a base skeleton will be created but manual\n" 96 " configuration wil be still needed to make the new vserver work\n" 97 " -n ... do not modify anything; just show what there will be\n" 98 " done (in combination with '-v')\n" 99 " -v ... verbose mode\n" 100 " -x ... do not cross filesystems; this is valid in manual\n" 101 " mode only and will be ignored for vserver unification\n\n" 102 "Please report bugs to " PACKAGE_BUGREPORT "\n"); 103 exit(res); 104 } 105 106 static void 107 showVersion() 108 { 109 WRITE_MSG(1, 110 "vcopy " VERSION " -- copies directories and vserver files\n" 111 "This program is part of " PACKAGE_STRING "\n\n" 112 "Copyright (C) 2003,2004 Enrico Scholz\n" 113 VERSION_COPYRIGHT_DISCLAIMER); 114 exit(0); 115 } 116 117 int Global_getVerbosity() { 118 return global_args->verbosity; 119 } 120 121 bool Global_doRenew() { 122 return true; 123 } 124 125 #include "vserver-visitdir.hc" 126 127 static Operation 128 checkDirEntry(PathInfo const *path, struct stat const *st) 129 { 130 struct WalkdownInfo const * const info = &global_info; 131 MatchType res; 132 133 // when marked as 'skip' in the first excludelist already, we do not need to 134 // visit the second one since it could not change that. 135 res=MatchList_compare(&info->dst_list, path->d); 136 if (res!=stSKIP) { 137 MatchType tmp = MatchList_compare(&info->src_list, path->d); 138 139 // stINCLUDE gets overridden by stEXCLUDE+stSKIP, and stEXCLUDE by stSKIP. 140 // Using the MAX() macro is a hack but it works 141 res=MAX(res,tmp); 142 } 143 144 // non-skipped directories are marked as opDIR 145 if (res!=stSKIP && S_ISDIR(st->st_mode)) 146 return opDIR; 147 148 // non-skipped symlinks will be copied always 149 if (res!=stSKIP && S_ISLNK(st->st_mode)) 150 return opCOPY; 151 152 // skipped files or non regular files (character/block devices) will be skipped 153 // always 154 if (res==stSKIP || !S_ISREG(st->st_mode)) 155 return opSKIP; 156 157 switch (res) { 158 case stINCLUDE : return opUNIFY; 159 case stEXCLUDE : return opCOPY; 160 case stSKIP : assert(false); // already handled above 161 default : assert(false); abort(); 162 } 163 } 164 165 static bool 166 doit(Operation op, 167 PathInfo const *dst_path, 168 PathInfo const *src_path, struct stat const *exp_stat, 169 PathInfo const *show_path) 170 { 171 #if 0 172 struct stat st; 173 174 if (lstat(dst_path->d, &st)!=-1) { 175 if (global_args->do_keep && 176 (!S_ISDIR(exp_stat->st_mode) || S_ISDIR(st.st_mode))) { 177 // when keep-mode is enable and, do nothing 178 if (global_args->do_dry_run || global_args->verbosity>1) { 179 WRITE_MSG(1, "keeping '"); 180 write(1, show_path->d, show_path->l); 181 WRITE_MSG(1, "'\n"); 182 } 183 return true; 184 } 185 186 } 187 #endif 188 189 if (global_args->do_dry_run || global_args->verbosity>1) { 190 if (op==opUNIFY) WRITE_MSG(1, "linking '"); 191 else if (op==opCOPY) WRITE_MSG(1, "copying '"); 192 else if (op==opDIR) WRITE_MSG(1, "creating '"); 193 else if (op==opSKIP) WRITE_MSG(1, "skipping '"); 194 else { assert(false); abort(); } 195 196 Vwrite(1, show_path->d, show_path->l); 197 WRITE_MSG(1, "'\n"); 198 } 199 200 return (global_args->do_dry_run || 201 ( op==opSKIP) || 202 ( op==opUNIFY && Unify_unify(src_path->d, exp_stat, dst_path->d, false)) || 203 ((op==opCOPY || 204 op==opDIR) && Unify_copy (src_path->d, exp_stat, dst_path->d))); 205 } 206 207 static uint64_t 208 visitDirEntry(struct dirent const *ent) 209 { 210 char const * dirname = ent->d_name; 211 if (isDotfile(dirname)) return 0; 212 213 uint64_t res = 1; 214 PathInfo src_path = global_info.state; 215 PathInfo src_d_path = { 216 .d = dirname, 217 .l = strlen(dirname) 218 }; 219 char path_buf[ENSC_PI_APPSZ(src_path, src_d_path)]; 220 struct stat f_stat = { .st_dev = 0 }; 221 222 PathInfo_append(&src_path, &src_d_path, path_buf); 223 224 225 if (lstat(dirname, &f_stat)==-1) 226 perror("lstat()"); 227 else { 228 Operation op = checkDirEntry(&src_path, &f_stat); 229 PathInfo dst_path = global_info.dst_list.root; 230 char dst_path_buf[ENSC_PI_APPSZ(dst_path, src_path)]; 231 232 PathInfo_append(&dst_path, &src_path, dst_path_buf); 233 if (!doit(op, &dst_path, &src_d_path, &f_stat, &src_path)) 234 perror(src_path.d); 235 else if (op==opDIR) { 236 res = visitDir(dirname, &f_stat); 237 if (!global_args->do_dry_run && 238 !Unify_setTime(dst_path.d, &f_stat)) 239 perror("utime()"); 240 } 241 else 242 res = 0; 243 } 244 245 return res; 246 } 247 248 #include "vcopy-init.hc" 249 250 int main(int argc, char *argv[]) 251 { 252 struct Arguments args = { 253 .mode = mdVSERVER, 254 .do_dry_run = false, 255 .verbosity = 0, 256 .local_fs = false, 257 }; 258 uint64_t res; 259 260 global_args = &args; 261 while (1) { 262 int c = getopt_long(argc, argv, "nvcx", 263 CMDLINE_OPTIONS, 0); 264 if (c==-1) break; 265 266 switch (c) { 267 case CMD_HELP : showHelp(1, argv[0], 0); 268 case CMD_VERSION : showVersion(); 269 case CMD_MANUALLY : args.mode = mdMANUALLY; break; 270 case CMD_STRICT : args.is_strict = true; break; 271 case 'n' : args.do_dry_run = true; break; 272 case 'x' : args.local_fs = true; break; 273 case 'v' : ++args.verbosity; break; 274 default : 275 WRITE_MSG(2, "Try '"); 276 WRITE_STR(2, argv[0]); 277 WRITE_MSG(2, " --help' for more information.\n"); 278 return EXIT_FAILURE; 279 break; 280 } 281 } 282 283 if (argc==optind) { 284 WRITE_MSG(2, "No directory/vserver given\n"); 285 return EXIT_FAILURE; 286 } 287 288 switch (args.mode) { 289 case mdMANUALLY : initModeManually(argc-optind, argv+optind); break; 290 case mdVSERVER : initModeVserver (argc-optind, argv+optind); break; 291 default : assert(false); return EXIT_FAILURE; 292 } 293 294 if (global_args->verbosity>3) 295 WRITE_MSG(1, "Starting to traverse directories...\n"); 296 297 Echdir(global_info.src_list.root.d); 298 res = visitDir("/", 0); 299 300 #ifndef NDEBUG 301 { 302 MatchList_destroy(&global_info.dst_list); 303 MatchList_destroy(&global_info.src_list); 304 } 305 #endif 306 307 return res>0 ? 1 : 0; 308 }