Work with builds done in a separate directory.
[util-vserver.git] / src / vhashify-init.hc
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2005 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 #include "pathconfig.h"
19 #include "lib_internal/util-dotfile.h"
20
21 #include <sys/param.h>
22
23 static UNUSED void
24 freeHashList(HashDirCollection *hash_vec)
25 {
26   for (struct HashDirInfo *itm = Vector_begin(hash_vec);
27        itm!=Vector_end(hash_vec);
28        ++itm) {
29     free(const_cast(char *)(itm->path.d));
30   }
31
32   Vector_free(hash_vec);
33 }
34
35 static size_t
36 initHashList(HashDirCollection *hash_vec, char const *hashdir)
37 {
38   int           cur_dir = Eopen(".", O_RDONLY|O_DIRECTORY, 0);
39   Echdir(hashdir);
40   
41   DIR           *d      = Eopendir(".");
42   struct dirent *ep;
43   size_t        l   = strlen(hashdir);
44   size_t        res = 0;
45
46   while ((ep=readdir(d)) != 0) {
47     struct stat         st;
48
49     if (isDotfile(ep->d_name) ||
50         stat(ep->d_name, &st)==-1 || !S_ISDIR(st.st_mode))
51       continue;
52
53     if (HashDirInfo_findDevice(hash_vec, st.st_dev)!=0) {
54       WRITE_MSG(2, "Duplicate hash-dir entry '");
55       WRITE_STR(2, ep->d_name);
56       WRITE_MSG(2, "' found\n");
57       continue;
58     }
59
60     char                *full_path = Emalloc(l + strlen(ep->d_name) + 3);
61     char                *ptr       = full_path + l;
62
63     memcpy(full_path, hashdir, l);
64     while (ptr>full_path && ptr[-1]=='/') --ptr;
65     *ptr++ = '/';
66     strcpy(ptr, ep->d_name);
67     strcat(ptr, "/");   // append a trailing '/'
68
69
70     struct HashDirInfo  tmp = {
71       .device  = st.st_dev,
72       .path    = { full_path, strlen(full_path) },
73     };
74
75     res = MAX(res, tmp.path.l);
76     
77     memcpy(Vector_pushback(hash_vec), &tmp, sizeof tmp);
78   }
79
80   if (Vector_count(hash_vec)==0) {
81     WRITE_MSG(2, "Could not find a place for the hashified files at '");
82     WRITE_STR(2, hashdir);
83     WRITE_MSG(2, "'.\n");
84     exit(wrapper_exit_code);
85   }
86
87   Eclosedir(d);
88   Efchdir(cur_dir);
89   Eclose(cur_dir);
90
91   return res;
92 }
93
94 static bool
95 initHashMethod(struct HashDirConfiguration *conf, char const *filename)
96 {
97   int           fd = open(filename, O_RDONLY);
98   if (fd==-1 && conf->method==0)
99     conf->method = ensc_crypto_hash_get_default();
100
101   if (fd==-1) {
102     char const  *hash_name;
103     assert(conf->method!=0);
104     if (conf->method==0)      return false;
105     if (global_args->dry_run) return true;      // do not create the file
106
107     fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
108
109     hash_name = ensc_crypto_hash_get_name(conf->method);
110     TEMP_FAILURE_RETRY(write(fd, hash_name, strlen(hash_name)));
111     TEMP_FAILURE_RETRY(write(fd, "\n", 1));
112   }
113   else {
114     off_t       s  = Elseek(fd, 0, SEEK_END);
115     char        buf[s + 1];
116     Elseek(fd, 0, SEEK_SET);
117
118     conf->method=0;
119
120     if (s>0 && read(fd, buf, s+1)==s) {
121       while (s>0 && (buf[s-1]=='\0' || buf[s-1]=='\n'))
122         --s;
123       buf[s] = '\0';
124
125       conf->method = ensc_crypto_hash_find(buf);
126       if (conf->method==0) {
127         WRITE_MSG(2, "Can not find hash-function '");
128         WRITE_STR(2, buf);
129         WRITE_MSG(2, "'\n");
130       }
131     }
132     else
133       WRITE_MSG(2, "Can not read configuration file for hash-method\n");
134   }
135
136   if (conf->method!=0 && ensc_crypto_hash_get_digestsize(conf->method)*8>HASH_MAXBITS) {
137     WRITE_MSG(2, "Wow... what an huge hash-function. I can not handle so much bits; giving up...\n");
138     conf->method=0;
139   }
140   
141   Eclose(fd);
142   return conf->method!=0;
143 }
144
145 static bool
146 initHashBlocks(struct HashDirConfiguration *conf, char const *filename)
147 {
148   int           fd = open(filename, O_RDONLY);
149
150   if (fd==-1) {
151     char                str[sizeof("all,start,middle,end,")] = { [0] = '\0' };
152
153     if (global_args->dry_run) return true;      // do not create the file
154     
155     fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
156
157     if (conf->blocks== hshALL)    strcat(str, "all\n");
158     if (conf->blocks & hshSTART)  strcat(str, "start\n");
159     if (conf->blocks & hshMIDDLE) strcat(str, "middle\n");
160     if (conf->blocks & hshEND)    strcat(str, "end\n");
161
162     EwriteAll(fd, str, strlen(str));
163   }
164   else {
165     off_t       s  = Elseek(fd, 0, SEEK_END);
166     char        buf[s + 1];
167     Elseek(fd, 0, SEEK_SET);
168     
169     conf->blocks = hshINVALID;
170       
171     if (s>0 && read(fd, buf, s+1)==s) {
172       char      *tok = buf;
173       char      *sep = "\n,\t ";
174
175       buf[s]       = '\0';
176       conf->blocks = hshALL;
177
178       do {
179         char    *ptr = strsep(&tok, sep);
180
181         if (*ptr=='#') { sep = "\n"; continue; }
182         sep = "\n,\t ";
183         if (*ptr=='\0') continue;
184
185         if      (strcasecmp(ptr, "all")   ==0) conf->blocks  = hshALL;
186         else {
187           if (conf->blocks==hshINVALID) conf->blocks = 0;
188           
189           else if (strcasecmp(ptr, "start") ==0) conf->blocks |= hshSTART;
190           else if (strcasecmp(ptr, "middle")==0) conf->blocks |= hshMIDDLE;
191           else if (strcasecmp(ptr, "end")   ==0) conf->blocks |= hshEND;
192           else {
193             WRITE_MSG(2, "Invalid block descriptor '");
194             WRITE_STR(2, ptr);
195             WRITE_MSG(2, "'\n");
196             conf->blocks = hshINVALID;
197             tok = 0;
198           }
199         }
200       } while (tok!=0);
201     }
202     else
203       WRITE_MSG(2, "Can not read configuration file for hash-blocks\n");
204   }
205
206   Eclose(fd);
207   return conf->blocks!=hshINVALID;
208 }
209
210 static bool
211 initHashBlockSize(struct HashDirConfiguration *conf, char const *filename)
212 {
213   if (conf->blocks==hshALL) return true;
214   
215   int           fd = open(filename, O_RDONLY);
216   if (fd==-1) {
217     char                str[sizeof("0x") + sizeof(size_t)*3+2] = {
218       [0] = '0', [1] = 'x'
219     };
220     size_t              len = utilvserver_fmt_xuint(str+2, conf->blocksize);
221
222     if (global_args->dry_run) return true;      // do not create the file
223
224     fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
225     EwriteAll(fd, str, len+2);
226   }
227   else {
228     off_t       s  = Elseek(fd, 0, SEEK_END);
229     char        buf[s + 1];
230     Elseek(fd, 0, SEEK_SET);
231
232     conf->blocksize = (size_t)(-1);
233
234     if (s>0 && read(fd, buf, s+1)==s) {
235       char      *errptr;
236       
237       while (s>0 && (buf[s-1]=='\0' || buf[s-1]=='\n'))
238         --s;
239       buf[s] = '\0';
240
241       conf->blocksize = strtol(buf, &errptr, 0);
242       if (errptr==buf || (*errptr!='\0' && *errptr!='\n')) {
243         WRITE_MSG(2, "Failed to parse blocksize '");
244         WRITE_STR(2, buf);
245         WRITE_MSG(2, "'\n");
246         conf->blocksize = (size_t)(-1);
247       }
248     }
249     else
250       WRITE_MSG(2, "Can not read configuration file for hash-blocksize\n");
251   }
252
253   Eclose(fd);
254   return conf->blocksize!=(size_t)(-1);
255 }
256
257 static bool
258 initHashConf(struct HashDirConfiguration *conf, char const *hashdir)
259 {
260   size_t                l = strlen(hashdir);
261   char                  tmp[l + MAX(MAX(sizeof("/method"), sizeof("/blocks")),
262                                     sizeof("/blocksize"))];
263
264   memcpy(tmp, hashdir, l);
265
266   return ((strcpy(tmp+l, "/method"),    initHashMethod   (conf, tmp)) &&
267           (strcpy(tmp+l, "/blocks"),    initHashBlocks   (conf, tmp)) &&
268           (strcpy(tmp+l, "/blocksize"), initHashBlockSize(conf, tmp)));
269 }
270
271 static char *
272 searchHashdir(char const *lhs, char const *rhs)
273 {
274   size_t        l1  = strlen(lhs);
275   size_t        l2  = rhs ? strlen(rhs) : 0;
276   char *        res = Emalloc(l1 + l2 + 1);
277   struct stat   st;
278
279   strcpy(res, lhs);
280   if (rhs) strcat(res, rhs);
281
282   if (stat(res, &st)==-1 || !S_ISDIR(st.st_mode)) {
283     free(res);
284     res = 0;
285   }
286
287   return res;
288 }
289
290 static void
291 initModeManually(struct Arguments const UNUSED *args, int argc, char *argv[])
292 {
293   assert(args->hash_dir!=0);
294   
295   if (argc<2) {
296     WRITE_MSG(2, "No exclude list specified\n");
297     exit(1);
298   }
299
300   if (!initHashConf(&global_info.hash_conf, args->hash_dir)) {
301     WRITE_MSG(2, "failed to initialize hash-configuration\n");
302     exit(1);
303   }
304   
305   global_info.hash_dirs_max_size = initHashList(&global_info.hash_dirs,
306                                                 args->hash_dir);
307   MatchList_initManually(&global_info.dst_list, 0, strdup(argv[0]), argv[1]);
308 }
309
310 static void
311 initModeVserver(struct Arguments const UNUSED *args, int argc, char *argv[])
312 {
313   char const                            *hashdir   = args->hash_dir;
314   struct MatchVserverInfo               vserver = {
315     .name        = argv[0],
316     .use_pkgmgmt = true
317   };
318
319   if (!MatchVserverInfo_init(&vserver)) {
320     WRITE_MSG(2, "Failed to initialize unification for vserver\n");
321     exit(1);
322   }
323
324   if (argc!=1) {
325     WRITE_MSG(2, "More than one vserver is not supported\n");
326     exit(1);
327   }
328
329   if (!MatchList_initByVserver(&global_info.dst_list, &vserver)) {
330     WRITE_MSG(2, "unification not configured for this vserver\n");
331     exit(1);
332   }
333
334   if (hashdir==0) hashdir = searchHashdir(vserver.appdir.d, "/hash");
335   if (hashdir==0) hashdir = searchHashdir(CONFDIR "/.defaults/apps/vunify/hash", 0);
336
337   if (hashdir==0) {
338     WRITE_MSG(2, "no hash-directory configured for this vserver.\n");
339     exit(1);
340   }
341
342   if (!initHashConf(&global_info.hash_conf, hashdir)) {
343     WRITE_MSG(2, "failed to initialize hash-configuration\n");
344     exit(1);
345   }
346   
347   global_info.hash_dirs_max_size = initHashList(&global_info.hash_dirs, hashdir);
348
349   free(const_cast(char *)(hashdir));
350   MatchVserverInfo_free(&vserver);
351 }
352