3 // Copyright (C) 2007 Daniel Hokka Zakrisson
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.
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.
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.
26 #include "lib_internal/pathinfo.h"
27 #include "lib_internal/unify.h"
28 #include "lib_internal/matchlist.h"
38 #include <sys/param.h>
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
47 #define CMD_HELP 0x8000
48 #define CMD_VERSION 0x8001
49 #define CMD_XID 0x8002
56 struct MatchList excludes;
60 unsigned int verbosity;
62 const char * exclude_list;
65 static struct WalkdownInfo global_info;
66 static struct Arguments const * global_args;
68 int wrapper_exit_code = 1;
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' },
81 showHelp(int fd, char const *cmd, int res)
83 VSERVER_DECLARE_CMD(cmd);
85 WRITE_MSG(fd, "Usage:\n ");
88 " [--xid <xid>] [--exclude-from <exclude-list>]\n"
89 " <source> <absolute path to destination>\n\n"
90 "Please report bugs to " PACKAGE_BUGREPORT "\n");
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);
105 int Global_getVerbosity() {
106 return global_args->verbosity;
109 bool Global_doRenew() {
113 #include "vserver-visitdir.hc"
116 handleDirEntry(const PathInfo *src_path, const PathInfo *basename,
117 bool *is_dir, struct stat *st)
123 if (lstat(basename->d, st)==-1)
124 PERROR_Q(ENSC_WRAPPERS_PREFIX "lstat", src_path->d);
126 PathInfo dst_path = global_info.dst;
127 char dst_path_buf[ENSC_PI_APPSZ(dst_path, *src_path)];
130 if (S_ISDIR(st->st_mode))
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");
142 PathInfo_append(&dst_path, src_path, dst_path_buf);
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");
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);
161 if (mkdirRecursive(dst_path.d) == -1)
162 PERROR_Q(ENSC_WRAPPERS_PREFIX "mkdirRecursive", dst_path.d);
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);
172 Elink(basename->d, dst_path.d);
175 /* something we have to copy */
177 if (Global_getVerbosity() > 2) {
178 WRITE_MSG(1, " copying non-unified file '");
179 Vwrite(1, src_path->d, src_path->l);
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 &&
188 PERROR_Q(ENSC_WRAPPERS_PREFIX "vc_set_iattr", dst_path.d);
198 /* returns 1 on error, 0 on success */
200 visitDirEntry(struct dirent const *ent)
202 char const * dirname = ent->d_name;
203 if (isDotfile(dirname)) return 0;
206 PathInfo src_path = global_info.state;
207 PathInfo src_d_path = {
211 char path_buf[ENSC_PI_APPSZ(src_path, src_d_path)];
212 struct stat f_stat = { .st_dev = 0 };
215 PathInfo_append(&src_path, &src_d_path, path_buf);
217 if (handleDirEntry(&src_path, &src_d_path, &is_dir, &f_stat))
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--;
231 int main(int argc, char *argv[])
233 struct Arguments args = {
236 .exclude_list = NULL,
243 int c = getopt_long(argc, argv, "+vX:",
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;
254 WRITE_MSG(2, "Try '");
255 WRITE_STR(2, argv[0]);
256 WRITE_MSG(2, " --help' for more information.\n");
262 num_args = argc - optind;
264 WRITE_MSG(2, "Source is missing; try '");
265 WRITE_STR(2, argv[0]);
266 WRITE_MSG(2, " --help' for more information.\n");
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");
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");
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");
287 ENSC_PI_SETSTR(global_info.src, argv[optind]);
288 ENSC_PI_SETSTR(global_info.dst, argv[optind+1]);
290 if (global_args->exclude_list)
291 MatchList_initManually(&global_info.excludes, 0, strdup(argv[optind]),
292 global_args->exclude_list);
294 MatchList_init(&global_info.excludes, argv[optind], 0);
296 if (global_args->verbosity>3)
297 WRITE_MSG(1, "Starting to traverse directories...\n");
299 Echdir(global_info.src.d);
300 res = visitDir("/", 0);
302 MatchList_destroy(&global_info.excludes);
304 return res>0 ? 1 : 0;