]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
ebl Cleanup accurate code
[bacula/bacula] / bacula / src / filed / accurate.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Version $Id$
30  *
31  */
32
33 #include "bacula.h"
34 #include "filed.h"
35
36 static void realfree(void *p);  /* used by tokyo code */
37 static int dbglvl=200;
38
39 typedef struct PrivateCurFile {
40 #ifndef USE_TCHDB
41    hlink link;
42 #endif
43    char *fname;                 /* not stored with tchdb mode */
44    time_t ctime;
45    time_t mtime;
46    bool seen;
47 } CurFile;
48
49 #ifdef USE_TCHDB
50
51 /*
52  * Update hash element seen=1
53  */
54 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
55 {
56    bool ret=true;
57
58    elt->seen = 1;
59    if (!tchdbputasync(jcr->file_list, 
60                       elt->fname, strlen(elt->fname)+1, 
61                       elt, sizeof(CurFile)))
62    { /* TODO: disabling accurate mode ?  */
63       Jmsg(jcr, M_ERROR, 1, _("Can't update accurate hash disk ERR=%s\n"),
64            tchdberrmsg(tchdbecode(jcr->file_list)));
65       ret = false;
66    }
67
68    return ret;
69 }
70
71 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
72 {
73    bool found=false;
74    ret->seen = 0;
75
76    if (tchdbget3(jcr->file_list, 
77                  fname, strlen(fname)+1, 
78                  ret, sizeof(CurFile)) != -1)
79    {
80       found = true;
81       ret->fname = fname;
82 //    Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
83    }
84    return found;
85 }
86
87 /* Create tokyo dbm hash file 
88  * If something goes wrong, we cancel accurate mode.
89  */
90 static bool accurate_init(JCR *jcr, int nbfile)
91 {
92    jcr->file_list = tchdbnew();
93    tchdbsetcache(jcr->file_list, 300000);
94    tchdbtune(jcr->file_list,
95              nbfile,            /* nb bucket 0.5n to 4n */
96              6,                 /* size of element 2^x */
97              16,
98              0);                /* options like compression */
99
100    POOLMEM *name  = get_pool_memory(PM_MESSAGE);
101    make_unique_filename(name, jcr->JobId, "accurate");
102
103    if(!tchdbopen(jcr->file_list, name, HDBOWRITER | HDBOCREAT)){
104       Jmsg(jcr, M_ERROR, 1, _("Can't open accurate hash disk ERR=%s\n"),
105            tchdberrmsg(tchdbecode(jcr->file_list)));
106       Jmsg(jcr, M_INFO, 1, _("Disabling accurate mode\n"));
107       tchdbdel(jcr->file_list);
108       jcr->file_list = NULL;
109       jcr->accurate = false;
110    }
111    free_pool_memory(name);
112    return jcr->file_list != NULL;
113 }
114
115 /* This function is called at the end of backup
116  * We walk over all hash disk element, and we check
117  * for elt.seen.
118  */
119 bool accurate_send_deleted_list(JCR *jcr)
120 {
121    char *key;
122    CurFile elt;
123    FF_PKT *ff_pkt;
124    int stream = STREAM_UNIX_ATTRIBUTES;
125
126    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
127       goto bail_out;
128    }
129
130    if (jcr->file_list == NULL) {
131       goto bail_out;
132    }
133
134    ff_pkt = init_find_files();
135    ff_pkt->type = FT_DELETED;
136
137    /* traverse records */
138    tchdbiterinit(jcr->file_list);
139    while((key = tchdbiternext2(jcr->file_list)) != NULL) {
140       if (tchdbget3(jcr->file_list, 
141                     key, strlen(key)+1, 
142                     &elt, sizeof(CurFile)) != -1)
143       {
144          if (!elt.seen) {
145             ff_pkt->fname = key;
146             ff_pkt->statp.st_mtime = elt.mtime;
147             ff_pkt->statp.st_ctime = elt.ctime;
148             encode_and_send_attributes(jcr, ff_pkt, stream);
149          }
150          realfree(key);         /* tokyo cabinet have to use real free() */
151       }
152    }
153
154    term_find_files(ff_pkt);
155 bail_out:
156    /* TODO: clean htable when this function is not reached ? */
157    if (jcr->file_list) {
158       Dmsg2(dbglvl, "Accurate hash size=%lli\n",tchdbfsiz(jcr->file_list));
159       if(!tchdbclose(jcr->file_list)){
160          Jmsg(jcr, M_ERROR, 1, _("Can't close accurate hash disk ERR=%s\n"),
161               tchdberrmsg(tchdbecode(jcr->file_list)));
162       }
163
164       /* delete the object */
165       tchdbdel(jcr->file_list);
166
167       POOLMEM *name  = get_pool_memory(PM_MESSAGE);
168       make_unique_filename(name, jcr->JobId, "accurate");
169       unlink(name);
170
171       free_pool_memory(name);
172       jcr->file_list = NULL;
173    }
174    return true;
175 }
176
177 #else  /* HTABLE mode */
178
179 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
180 {
181    CurFile *temp = (CurFile *)jcr->file_list->lookup(elt->fname);
182    temp->seen = 1;              /* records are in memory */
183    return true;
184 }
185
186 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
187 {
188    bool found=false;
189    ret->seen = 0;
190
191    CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
192    if (temp) {
193       memcpy(ret, temp, sizeof(CurFile));
194       found=true;
195 //    Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
196    }
197
198    return found;
199 }
200
201 static bool accurate_init(JCR *jcr, int nbfile)
202 {
203    CurFile *elt=NULL;
204    jcr->file_list = (htable *)malloc(sizeof(htable));
205    jcr->file_list->init(elt, &elt->link, nbfile);
206    return true;
207 }
208
209 /* This function is called at the end of backup
210  * We walk over all hash disk element, and we check
211  * for elt.seen.
212  */
213 bool accurate_send_deleted_list(JCR *jcr)
214 {
215    CurFile *elt;
216    FF_PKT *ff_pkt;
217    int stream = STREAM_UNIX_ATTRIBUTES;
218
219    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
220       goto bail_out;
221    }
222
223    if (jcr->file_list == NULL) {
224       goto bail_out;
225    }
226
227    ff_pkt = init_find_files();
228    ff_pkt->type = FT_DELETED;
229
230    foreach_htable (elt, jcr->file_list) {
231       if (!elt->seen) { /* already seen */
232          Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
233          ff_pkt->fname = elt->fname;
234          ff_pkt->statp.st_mtime = elt->mtime;
235          ff_pkt->statp.st_ctime = elt->ctime;
236          encode_and_send_attributes(jcr, ff_pkt, stream);
237       }
238 //      free(elt->fname);
239    }
240
241    term_find_files(ff_pkt);
242 bail_out:
243    /* TODO: clean htable when this function is not reached ? */
244    if (jcr->file_list) {
245       jcr->file_list->destroy();
246       free(jcr->file_list);
247       jcr->file_list = NULL;
248    }
249    return true;
250 }
251
252 #endif
253
254 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
255 {
256    bool ret = true;
257    CurFile elt;
258    struct stat statp;
259    int LinkFIc;
260    decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
261    elt.ctime = statp.st_ctime;
262    elt.mtime = statp.st_mtime;
263    elt.seen = 0;
264
265 #ifdef USE_TCHDB
266    if (!tchdbputasync(jcr->file_list,
267                       fname, strlen(fname)+1,
268                       &elt, sizeof(CurFile)))
269    {
270       Jmsg(jcr, M_ERROR, 1, _("Can't update accurate hash disk ERR=%s\n"),
271            tchdberrmsg(tchdbecode(jcr->file_list)));
272       ret = false;
273    }
274 #else  /* HTABLE */
275    CurFile *item;
276    /* we store CurFile, fname and ctime/mtime in the same chunk */
277    item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+1);
278    memcpy(item, &elt, sizeof(CurFile));
279    item->fname  = (char *)item+sizeof(CurFile);
280    strcpy(item->fname, fname);
281    jcr->file_list->insert(item->fname, item); 
282 #endif
283
284 // Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
285    return ret;
286 }
287
288 /*
289  * This function is called for each file seen in fileset.
290  * We check in file_list hash if fname have been backuped
291  * the last time. After we can compare Lstat field. 
292  * Full Lstat usage have been removed on 6612 
293  */
294 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
295 {
296    bool stat = false;
297    char *fname;
298    CurFile elt;
299
300    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
301       return true;
302    }
303
304    strip_path(ff_pkt);
305  
306    if (S_ISDIR(ff_pkt->statp.st_mode)) {
307       fname = ff_pkt->link;
308    } else {
309       fname = ff_pkt->fname;
310    } 
311
312    if (!accurate_lookup(jcr, fname, &elt)) {
313       Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
314       stat = true;
315       goto bail_out;
316    }
317
318    if (elt.seen) { /* file has been seen ? */
319       Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
320       goto bail_out;
321    }
322
323    if (elt.mtime != ff_pkt->statp.st_mtime) {
324      Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
325      stat = true;
326    } else if (elt.ctime != ff_pkt->statp.st_ctime) {
327      Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
328      stat = true;
329    }
330
331    accurate_mark_file_as_seen(jcr, &elt);
332    Dmsg2(dbglvl, "accurate %s = %i\n", fname, stat);
333
334 bail_out:
335    unstrip_path(ff_pkt);
336    return stat;
337 }
338
339 /* 
340  * TODO: use bigbuffer from htable
341  */
342 int accurate_cmd(JCR *jcr)
343 {
344    BSOCK *dir = jcr->dir_bsock;
345    int len;
346    int32_t nb;
347
348    if (!jcr->accurate || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
349       return true;
350    }
351
352    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
353       dir->fsend(_("2991 Bad accurate command\n"));
354       return false;
355    }
356    Dmsg2(dbglvl, "nb=%d msg=%s\n", nb, dir->msg);
357
358    accurate_init(jcr, nb);
359
360    /*
361     * buffer = sizeof(CurFile) + dirmsg
362     * dirmsg = fname + \0 + lstat
363     */
364    /* get current files */
365    while (dir->recv() >= 0) {
366       len = strlen(dir->msg) + 1;
367       if (len < dir->msglen) {
368          accurate_add_file(jcr, dir->msg, dir->msg + len);
369       }
370    }
371
372 #ifdef DEBUG
373    extern void *start_heap;
374
375    char b1[50], b2[50], b3[50], b4[50], b5[50];
376    Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
377          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
378          edit_uint64_with_commas(sm_bytes, b2),
379          edit_uint64_with_commas(sm_max_bytes, b3),
380          edit_uint64_with_commas(sm_buffers, b4),
381          edit_uint64_with_commas(sm_max_buffers, b5));
382
383 #endif
384
385    return true;
386 }
387
388 /*
389  * Tokyo Cabinet library doesn't use smartalloc by default
390  * results need to be release with real free()
391  */
392 #undef free
393 void realfree(void *p)
394 {
395    free(p);
396 }