initial checkin
[util-vserver.git] / util-vserver / src / vunify.c
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2003 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 "vunify-matchlist.h"
24 #include "wrappers-dirent.h"
25 #include "vunify-operations.h"
26 #include "util.h"
27
28 #include <dirent.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <stdbool.h>
33
34 int     wrapper_exit_code = 1;
35
36 struct WalkdownInfo
37 {
38     PathInfo                    state;
39     struct MatchList            dst_list;
40     struct {
41         struct MatchList *      v;
42         size_t                  l;
43     }                           src_lists;
44 };
45
46
47 static struct WalkdownInfo      global_info;
48 static struct Operations        operations;
49
50 static void     visitDirEntry(struct dirent const *) NONNULL((1));
51 static void     visitDir(char const *, struct stat const *) NONNULL((1));
52 static bool     checkFstat(struct MatchList const * const,
53                            PathInfo const * const,
54                            PathInfo const * const,
55                            struct stat const ** const,
56                            struct stat * const) NONNULL((1,2,3,4,5));
57
58 static struct MatchList const *
59 checkDirEntry(PathInfo const *,
60               PathInfo const *,
61               bool *, struct stat *) NONNULL((1,2,3,4));
62
63 static bool     updateSkipDepth(PathInfo const *, bool) NONNULL((1));
64 static void     EsafeChdir(char const *, struct stat const *)  NONNULL((1,2));
65 static bool     doit(struct MatchList const *, PathInfo const *,
66                      char const *dst_path) NONNULL((1,2,3));
67
68
69
70 // Returns 'false' iff one of the files is not existing, or of the files are different/not unifyable
71 static bool
72 checkFstat(struct MatchList const * const mlist,
73            PathInfo const * const  basename,
74            PathInfo const * const  path,
75            struct stat const ** const result, struct stat * const result_buf)
76 {
77   assert(basename->d[0] != '/');
78
79   if (*result==0) {
80     // local file does not exist... strange
81     // TODO: message
82     if (lstat(basename->d, result_buf)==-1) return false;
83     *result = result_buf;
84   }
85
86   assert(*result!=0);
87   
88   {
89     struct stat         src_fstat;
90     PathInfo            src_path = mlist->root;
91     char                src_path_buf[ENSC_PI_APPSZ(src_path, *path)];
92
93     PathInfo_append(&src_path, path, src_path_buf);
94
95     // source file does not exist
96     if (lstat(src_path.d, &src_fstat)==-1) return false;
97
98     // both files are different, so return false
99     if (!(operations.compare)(*result, &src_fstat)) return false;
100   }
101
102   // these are the same files
103   return true;
104 }
105
106 static struct MatchList const *
107 checkDirEntry(PathInfo const *path,
108               PathInfo const *d_path, bool *is_dir, struct stat *f_stat)
109 {
110   struct WalkdownInfo const * const     info     = &global_info;
111   struct MatchList const *              mlist;
112   struct stat const *                   cache_stat = 0;
113
114   // Check if it is in the exclude/include list of the destination vserver and
115   // abort when it is not matching an allowed entry
116   if (!MatchList_compare(&info->dst_list, path->d)) return 0;
117
118   // Now, go through the reference vservers and do the lightweigt list-check
119   // first and compare then the fstat's.
120   for (mlist=info->src_lists.v; mlist<info->src_lists.v+info->src_lists.l; ++mlist) {
121     if (MatchList_compare(mlist, path->d) &&
122         checkFstat(mlist, d_path, path, &cache_stat, f_stat)) {
123
124       // Failed the check or is it a symlink which can not be handled
125       if (cache_stat==0 || S_ISLNK(f_stat->st_mode)) return 0;
126       
127       *is_dir = S_ISDIR(f_stat->st_mode);
128       return mlist;
129     }
130   }
131
132   // No luck...
133   return 0;
134 }
135
136 static bool
137 updateSkipDepth(PathInfo const *path, bool walk_down)
138 {
139   struct WalkdownInfo const * const     info   = &global_info;
140   struct MatchList *                    mlist;
141   bool                                  result = false;
142
143   for (mlist=info->src_lists.v; mlist<info->src_lists.v+info->src_lists.l; ++mlist) {
144     // The easy way... this path is being skipped already
145     if (mlist->skip_depth>0) {
146       if (walk_down) ++mlist->skip_depth;
147       else           --mlist->skip_depth;
148       continue;
149     }
150     else if (walk_down) {
151       PathInfo          src_path = mlist->root;
152       char              src_path_buf[ENSC_PI_APPSZ(src_path, *path)];
153       struct stat       src_fstat;
154
155       PathInfo_append(&src_path, path, src_path_buf);
156
157       // when the file/dir exist, we have do go deeper.
158       // else skip it in deeper runs for *this* matchlist
159       if (lstat(src_path.d, &src_fstat)!=-1) result = true;
160       else                                   ++mlist->skip_depth;
161     }
162     else {
163       // TODO: warning
164     }
165   }
166
167   return result;
168 }
169
170 inline static void
171 EsafeChdir(char const *path, struct stat const *exp_stat)
172 {
173   FatalErrnoError(safeChdir(path, exp_stat)==-1, "safeChdir()");
174 }
175
176 static bool
177 doit(struct MatchList const *mlist, PathInfo const *src_path,
178      char const *dst_path)
179 {
180   PathInfo      path = mlist->root;
181   char          path_buf[ENSC_PI_APPSZ(path, *src_path)];
182
183   PathInfo_append(&path, src_path, path_buf);
184   return (operations.doit)(path.d, dst_path);
185 }
186
187
188 static void
189 visitDirEntry(struct dirent const *ent)
190 {
191   bool                          is_dir;
192   struct MatchList const *      match;
193   struct stat                   f_stat;
194   char const *                  dirname  = ent->d_name;
195   size_t                        path_len = strlen(ent->d_name);
196   PathInfo                      path     = global_info.state;
197   PathInfo                      d_path = {
198     .d = dirname,
199     .l = path_len
200   };
201   char                          path_buf[ENSC_PI_APPSZ(path, d_path)];
202
203   PathInfo_append(&path, &d_path, path_buf);
204
205   if ((dirname[0]=='.' &&
206        (dirname[1]=='\0' || (dirname[1]=='.' && dirname[2]=='\0'))) ||
207       (match=checkDirEntry(&path, &d_path, &is_dir, &f_stat))==0) {
208     WRITE_MSG(1, "skipping '");
209     WRITE_STR(1, dirname);
210     WRITE_MSG(1, "'\n");
211     return;
212   }
213
214   if (is_dir) {
215     if (updateSkipDepth(&path, true)) {
216       visitDir(dirname, &f_stat);
217       updateSkipDepth(&path, false);
218     }
219   }
220   else if (!doit(match, &path, dirname)) {
221       // TODO: message
222   }
223 }
224
225 static void
226 visitDir(char const *name, struct stat const *expected_stat)
227 {
228   int           fd = Eopen(".", O_RDONLY, 0);
229   PathInfo      old_state = global_info.state;
230   PathInfo      rhs_path = {
231     .d = name,
232     .l = strlen(name)
233   };
234   char          new_path[ENSC_PI_APPSZ(global_info.state, rhs_path)];
235   DIR *         dir;
236
237   PathInfo_append(&global_info.state, &rhs_path, new_path);
238
239   if (expected_stat!=0)
240     EsafeChdir(name, expected_stat);
241   
242   dir = Eopendir(".");
243
244   for (;;) {
245     struct dirent               *ent = Ereaddir(dir);
246     if (ent==0) break;
247
248     visitDirEntry(ent);
249   }
250
251   Eclosedir(dir);
252
253   Efchdir(fd);
254   Eclose(fd);
255
256   global_info.state = old_state;
257 }
258
259
260 int main(int argc, char *argv[])
261 {
262   global_info.state.d = "";
263   global_info.state.l = 0;
264
265   Operations_init(&operations, opUNIFY, false);
266   MatchList_init(&global_info.dst_list, argv[1], 0);
267
268   global_info.src_lists.v = malloc(sizeof(struct MatchList));
269   global_info.src_lists.l = 1;
270   MatchList_init(global_info.src_lists.v+0, argv[2], 0);
271
272   Echdir(global_info.dst_list.root.d);
273   visitDir("/", 0);
274 }