Tagging module util-vserver - util-vserver-0.30.215-6
[util-vserver.git] / src / vclone.c
1 // $Id: vclone.c 2646 2007-12-20 00:09:06Z dhozac $    --*- 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 (global_args->xid != VC_NOCTX &&
184                  vc_set_iattr(dst_path.d, global_args->xid, 0, VC_IATTR_XID) == -1 &&
185                  errno != EINVAL)
186           PERROR_Q(ENSC_WRAPPERS_PREFIX "vc_set_iattr", dst_path.d);
187         else
188           res = true;
189       }
190     }
191   }
192
193   return res;
194 }
195
196 /* returns 1 on error, 0 on success */
197 static uint64_t
198 visitDirEntry(struct dirent const *ent)
199 {
200   char const *                  dirname  = ent->d_name;
201   if (isDotfile(dirname)) return 0;
202
203   uint64_t                      res      = 1;
204   PathInfo                      src_path = global_info.state;
205   PathInfo                      src_d_path = {
206     .d = dirname,
207     .l = strlen(dirname)
208   };
209   char                          path_buf[ENSC_PI_APPSZ(src_path, src_d_path)];
210   struct stat                   f_stat = { .st_dev = 0 };
211   bool                          is_dir;
212
213   PathInfo_append(&src_path, &src_d_path, path_buf);
214
215   if (handleDirEntry(&src_path, &src_d_path, &is_dir, &f_stat))
216     res = 0;
217
218   if (is_dir) {
219     if (res || global_info.excludes.skip_depth > 0)
220       global_info.excludes.skip_depth++;
221     res = res + visitDir(dirname, &f_stat);
222     if (global_info.excludes.skip_depth > 0)
223       global_info.excludes.skip_depth--;
224   }
225
226   return res;
227 }
228
229 int main(int argc, char *argv[])
230 {
231   struct Arguments      args = {
232     .verbosity          =  0,
233     .xid                = VC_NOCTX,
234     .exclude_list       = NULL,
235   };
236   uint64_t              res;
237   int                   num_args;
238
239   global_args = &args;
240   while (1) {
241     int         c = getopt_long(argc, argv, "+vX:",
242                                 CMDLINE_OPTIONS, 0);
243     if (c==-1) break;
244
245     switch (c) {
246       case CMD_HELP     :  showHelp(1, argv[0], 0);
247       case CMD_VERSION  :  showVersion();
248       case 'v'          :  args.verbosity++; break;
249       case 'X'          :  args.exclude_list = optarg; break;
250       case CMD_XID      :  args.xid = Evc_xidopt2xid(optarg,true); break;
251       default           :
252         WRITE_MSG(2, "Try '");
253         WRITE_STR(2, argv[0]);
254         WRITE_MSG(2, " --help' for more information.\n");
255         return EXIT_FAILURE;
256         break;
257     }
258   }
259
260   num_args = argc - optind;
261   if (num_args < 1) {
262     WRITE_MSG(2, "Source is missing; try '");
263     WRITE_STR(2, argv[0]);
264     WRITE_MSG(2, " --help' for more information.\n");
265     return EXIT_FAILURE;
266   }
267   else if (num_args < 2) {
268     WRITE_MSG(2, "Destination is missing; try '");
269     WRITE_STR(2, argv[0]);
270     WRITE_MSG(2, " --help' for more information.\n");
271     return EXIT_FAILURE;
272   }
273   else if (num_args > 2) {
274     WRITE_MSG(2, "Too many arguments; try '");
275     WRITE_STR(2, argv[0]);
276     WRITE_MSG(2, " --help' for more information.\n");
277     return EXIT_FAILURE;
278   }
279   else if (*argv[optind+1] != '/') {
280     WRITE_MSG(2, "The destination must be an absolute path; try '");
281     WRITE_STR(2, argv[0]);
282     WRITE_MSG(2, " --help' for more information.\n");
283     return EXIT_FAILURE;
284   }
285   ENSC_PI_SETSTR(global_info.src, argv[optind]);
286   ENSC_PI_SETSTR(global_info.dst, argv[optind+1]);
287
288   if (global_args->exclude_list)
289     MatchList_initManually(&global_info.excludes, 0, strdup(argv[optind]),
290                            global_args->exclude_list);
291   else
292     MatchList_init(&global_info.excludes, argv[optind], 0);
293
294   if (global_args->verbosity>3)
295     WRITE_MSG(1, "Starting to traverse directories...\n");
296
297   Echdir(global_info.src.d);
298   res = visitDir("/", 0);
299
300   MatchList_destroy(&global_info.excludes);
301   
302   return res>0 ? 1 : 0;
303 }