Remove the run file if the build fails.
[util-vserver.git] / src / vclone.c
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
129     if (S_ISDIR(st->st_mode))
130       *is_dir = true;
131
132     if (MatchList_compare(&global_info.excludes, src_path->d) != stINCLUDE) {
133       if (Global_getVerbosity() > 1) {
134         WRITE_MSG(1, "  skipping '");
135         Vwrite(1, src_path->d, src_path->l);
136         WRITE_MSG(1, "' (excluded)\n");
137       }
138       return false;
139     }
140
141     PathInfo_append(&dst_path, src_path, dst_path_buf);
142
143     /* skip files that already exist */
144     if (access(dst_path.d, F_OK)!=-1) {
145       if (Global_getVerbosity() > 1) {
146         WRITE_MSG(1, "  skipping '");
147         Vwrite(1, src_path->d, src_path->l);
148         WRITE_MSG(1, "' (exists in destination)\n");
149       }
150       res = true;
151     }
152     else {
153       /* create directory that might have been skipped */
154       if (global_info.excludes.skip_depth > 0) {
155         if (Global_getVerbosity() > 4) {
156           WRITE_MSG(1, "  creating directories for '");
157           Vwrite(1, dst_path.d, dst_path.l);
158           WRITE_MSG(1, "'\n");
159         }
160         if (mkdirRecursive(dst_path.d) == -1)
161           PERROR_Q(ENSC_WRAPPERS_PREFIX "mkdirRecursive", dst_path.d);
162       }
163
164       /* already unified file */
165       if (S_ISREG(st->st_mode) && Unify_isIUnlinkable(basename->d) == unifyBUSY) {
166         if (Global_getVerbosity() > 2) {
167           WRITE_MSG(1, "  linking unified file '");
168           Vwrite(1, src_path->d, src_path->l);
169           WRITE_MSG(1, "'\n");
170         }
171         Elink(basename->d, dst_path.d);
172         res = true;
173       }
174       /* something we have to copy */
175       else {
176         if (Global_getVerbosity() > 2) {
177           WRITE_MSG(1, "  copying non-unified file '");
178           Vwrite(1, src_path->d, src_path->l);
179           WRITE_MSG(1, "'\n");
180         }
181         if (!Unify_copy(basename->d, st, dst_path.d))
182           PERROR_Q(ENSC_WRAPPERS_PREFIX "Unify_copy", dst_path.d);
183         else if (!S_ISSOCK(st->st_mode) &&
184                  global_args->xid != VC_NOCTX &&
185                  vc_set_iattr(dst_path.d, global_args->xid, 0, VC_IATTR_XID) == -1 &&
186                  errno != EINVAL)
187           PERROR_Q(ENSC_WRAPPERS_PREFIX "vc_set_iattr", dst_path.d);
188         else
189           res = true;
190       }
191     }
192   }
193
194   return res;
195 }
196
197 /* returns 1 on error, 0 on success */
198 static uint64_t
199 visitDirEntry(struct dirent const *ent)
200 {
201   char const *                  dirname  = ent->d_name;
202   if (isDotfile(dirname)) return 0;
203
204   uint64_t                      res      = 1;
205   PathInfo                      src_path = global_info.state;
206   PathInfo                      src_d_path = {
207     .d = dirname,
208     .l = strlen(dirname)
209   };
210   char                          path_buf[ENSC_PI_APPSZ(src_path, src_d_path)];
211   struct stat                   f_stat = { .st_dev = 0 };
212   bool                          is_dir;
213
214   PathInfo_append(&src_path, &src_d_path, path_buf);
215
216   if (handleDirEntry(&src_path, &src_d_path, &is_dir, &f_stat))
217     res = 0;
218
219   if (is_dir) {
220     if (res || global_info.excludes.skip_depth > 0)
221       global_info.excludes.skip_depth++;
222     res = res + visitDir(dirname, &f_stat);
223     if (global_info.excludes.skip_depth > 0)
224       global_info.excludes.skip_depth--;
225   }
226
227   return res;
228 }
229
230 int main(int argc, char *argv[])
231 {
232   struct Arguments      args = {
233     .verbosity          =  0,
234     .xid                = VC_NOCTX,
235     .exclude_list       = NULL,
236   };
237   uint64_t              res;
238   int                   num_args;
239
240   global_args = &args;
241   while (1) {
242     int         c = getopt_long(argc, argv, "+vX:",
243                                 CMDLINE_OPTIONS, 0);
244     if (c==-1) break;
245
246     switch (c) {
247       case CMD_HELP     :  showHelp(1, argv[0], 0);
248       case CMD_VERSION  :  showVersion();
249       case 'v'          :  args.verbosity++; break;
250       case 'X'          :  args.exclude_list = optarg; break;
251       case CMD_XID      :  args.xid = Evc_xidopt2xid(optarg,true); break;
252       default           :
253         WRITE_MSG(2, "Try '");
254         WRITE_STR(2, argv[0]);
255         WRITE_MSG(2, " --help' for more information.\n");
256         return EXIT_FAILURE;
257         break;
258     }
259   }
260
261   num_args = argc - optind;
262   if (num_args < 1) {
263     WRITE_MSG(2, "Source is missing; try '");
264     WRITE_STR(2, argv[0]);
265     WRITE_MSG(2, " --help' for more information.\n");
266     return EXIT_FAILURE;
267   }
268   else if (num_args < 2) {
269     WRITE_MSG(2, "Destination is missing; try '");
270     WRITE_STR(2, argv[0]);
271     WRITE_MSG(2, " --help' for more information.\n");
272     return EXIT_FAILURE;
273   }
274   else if (num_args > 2) {
275     WRITE_MSG(2, "Too many arguments; try '");
276     WRITE_STR(2, argv[0]);
277     WRITE_MSG(2, " --help' for more information.\n");
278     return EXIT_FAILURE;
279   }
280   else if (*argv[optind+1] != '/') {
281     WRITE_MSG(2, "The destination must be an absolute path; try '");
282     WRITE_STR(2, argv[0]);
283     WRITE_MSG(2, " --help' for more information.\n");
284     return EXIT_FAILURE;
285   }
286   ENSC_PI_SETSTR(global_info.src, argv[optind]);
287   ENSC_PI_SETSTR(global_info.dst, argv[optind+1]);
288
289   if (global_args->exclude_list)
290     MatchList_initManually(&global_info.excludes, 0, strdup(argv[optind]),
291                            global_args->exclude_list);
292   else
293     MatchList_init(&global_info.excludes, argv[optind], 0);
294
295   if (global_args->verbosity>3)
296     WRITE_MSG(1, "Starting to traverse directories...\n");
297
298   Echdir(global_info.src.d);
299   res = visitDir("/", 0);
300
301   MatchList_destroy(&global_info.excludes);
302   
303   return res>0 ? 1 : 0;
304 }