f9...
[util-vserver.git] / src / vcopy.c
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 }