]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
ebl tweak debug level
[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 int dbglvl=200;
37
38 typedef struct PrivateCurFile {
39 #ifndef USE_TCADB
40    hlink link;
41 #endif
42    char *fname;                 /* not stored with tchdb mode */
43    time_t ctime;
44    time_t mtime;
45    bool seen;
46 } CurFile;
47
48 #ifdef USE_TCADB
49 static void realfree(void *p);  /* used by tokyo code */
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 (!tcadbput(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\n"));
64       ret = false;
65    }
66
67    return ret;
68 }
69
70 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
71 {
72    bool found=false;
73    ret->seen = 0;
74    int size;
75    CurFile *elt;
76
77    elt = (CurFile*)tcadbget(jcr->file_list, 
78                             fname, strlen(fname)+1, &size);
79    if (elt)
80    {
81       /* TODO: don't malloc/free results */
82       found = true;
83       elt->fname = fname;
84       memcpy(ret, elt, sizeof(CurFile));
85       realfree(elt);
86 //    Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
87    }
88    return found;
89 }
90
91 /* Create tokyo dbm hash file 
92  * If something goes wrong, we cancel accurate mode.
93  */
94 static bool accurate_init(JCR *jcr, int nbfile)
95 {
96    jcr->file_list = tcadbnew();
97 //
98 //   tchdbsetcache(jcr->file_list, 300000);
99 //   tchdbtune(jcr->file_list,
100 //           nbfile,            /* nb bucket 0.5n to 4n */
101 //           6,                 /* size of element 2^x */
102 //           16,
103 //           0);                /* options like compression */
104 //
105    jcr->hash_name  = get_pool_memory(PM_MESSAGE);
106    POOLMEM *temp = get_pool_memory(PM_MESSAGE);
107
108    if (nbfile > 500000) {
109       make_unique_filename(&jcr->hash_name, jcr->JobId, "accurate");
110       pm_strcat(jcr->hash_name, ".tcb");
111       Mmsg(temp, "%s#bnum=%i#mode=e#opts=l",
112            jcr->hash_name, nbfile*4); 
113       Dmsg1(dbglvl, "Doing accurate hash on disk %s\n", jcr->hash_name);
114    } else {
115       Dmsg0(dbglvl, "Doing accurate hash on memory\n");
116       pm_strcpy(jcr->hash_name, "*");
117       pm_strcpy(temp, "*");
118    }
119    
120    if(!tcadbopen(jcr->file_list, jcr->hash_name)){
121       Jmsg(jcr, M_ERROR, 1, _("Can't open accurate hash disk\n"));
122       Jmsg(jcr, M_INFO, 1, _("Disabling accurate mode\n"));
123       tcadbdel(jcr->file_list);
124       jcr->file_list = NULL;
125       jcr->accurate = false;
126    }
127    free_pool_memory(temp);
128    return jcr->file_list != NULL;
129 }
130
131 /* This function is called at the end of backup
132  * We walk over all hash disk element, and we check
133  * for elt.seen.
134  */
135 bool accurate_send_deleted_list(JCR *jcr)
136 {
137    char *key;
138    CurFile *elt;
139    int size;
140    FF_PKT *ff_pkt;
141    int stream = STREAM_UNIX_ATTRIBUTES;
142
143    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
144       goto bail_out;
145    }
146
147    if (jcr->file_list == NULL) {
148       goto bail_out;
149    }
150
151    ff_pkt = init_find_files();
152    ff_pkt->type = FT_DELETED;
153
154    /* traverse records */
155    tcadbiterinit(jcr->file_list);
156    while((key = tcadbiternext2(jcr->file_list)) != NULL) {
157       elt = (CurFile *) tcadbget(jcr->file_list, 
158                                  key, strlen(key)+1, &size);
159       if (elt)
160       {
161          if (!elt->seen) {      /* already seen */
162             ff_pkt->fname = key;
163             ff_pkt->statp.st_mtime = elt->mtime;
164             ff_pkt->statp.st_ctime = elt->ctime;
165             encode_and_send_attributes(jcr, ff_pkt, stream);
166          }
167          realfree(elt);
168       }
169       realfree(key);            /* tokyo cabinet have to use real free() */
170    }
171
172    term_find_files(ff_pkt);
173 bail_out:
174    /* TODO: clean htable when this function is not reached ? */
175    if (jcr->file_list) {
176       if(!tcadbclose(jcr->file_list)){
177          Jmsg(jcr, M_ERROR, 1, _("Can't close accurate hash disk\n"));
178       }
179
180       /* delete the object */
181       tcadbdel(jcr->file_list);
182       if (!bstrcmp(jcr->hash_name, "*")) {
183          unlink(jcr->hash_name);
184       }
185
186       free_pool_memory(jcr->hash_name);
187       jcr->hash_name = NULL;
188       jcr->file_list = NULL;
189    }
190    return true;
191 }
192
193 #else  /* HTABLE mode */
194
195 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
196 {
197    CurFile *temp = (CurFile *)jcr->file_list->lookup(elt->fname);
198    temp->seen = 1;              /* records are in memory */
199    return true;
200 }
201
202 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
203 {
204    bool found=false;
205    ret->seen = 0;
206
207    CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
208    if (temp) {
209       memcpy(ret, temp, sizeof(CurFile));
210       found=true;
211 //    Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
212    }
213
214    return found;
215 }
216
217 static bool accurate_init(JCR *jcr, int nbfile)
218 {
219    CurFile *elt=NULL;
220    jcr->file_list = (htable *)malloc(sizeof(htable));
221    jcr->file_list->init(elt, &elt->link, nbfile);
222    return true;
223 }
224
225 /* This function is called at the end of backup
226  * We walk over all hash disk element, and we check
227  * for elt.seen.
228  */
229 bool accurate_send_deleted_list(JCR *jcr)
230 {
231    CurFile *elt;
232    FF_PKT *ff_pkt;
233    int stream = STREAM_UNIX_ATTRIBUTES;
234
235    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
236       goto bail_out;
237    }
238
239    if (jcr->file_list == NULL) {
240       goto bail_out;
241    }
242
243    ff_pkt = init_find_files();
244    ff_pkt->type = FT_DELETED;
245
246    foreach_htable (elt, jcr->file_list) {
247       if (!elt->seen) { /* already seen */
248          Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
249          ff_pkt->fname = elt->fname;
250          ff_pkt->statp.st_mtime = elt->mtime;
251          ff_pkt->statp.st_ctime = elt->ctime;
252          encode_and_send_attributes(jcr, ff_pkt, stream);
253       }
254 //      free(elt->fname);
255    }
256
257    term_find_files(ff_pkt);
258 bail_out:
259    /* TODO: clean htable when this function is not reached ? */
260    if (jcr->file_list) {
261       jcr->file_list->destroy();
262       free(jcr->file_list);
263       jcr->file_list = NULL;
264    }
265    return true;
266 }
267
268 #endif /* common code */
269
270 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
271 {
272    bool ret = true;
273    CurFile elt;
274    struct stat statp;
275    int LinkFIc;
276    decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
277    elt.ctime = statp.st_ctime;
278    elt.mtime = statp.st_mtime;
279    elt.seen = 0;
280
281 #ifdef USE_TCADB
282    if (!tcadbput(jcr->file_list,
283                  fname, strlen(fname)+1,
284                  &elt, sizeof(CurFile)))
285    {
286       Jmsg(jcr, M_ERROR, 1, _("Can't update accurate hash disk ERR=%s\n"));
287       ret = false;
288    }
289 #else  /* HTABLE */
290    CurFile *item;
291    /* we store CurFile, fname and ctime/mtime in the same chunk */
292    item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+1);
293    memcpy(item, &elt, sizeof(CurFile));
294    item->fname  = (char *)item+sizeof(CurFile);
295    strcpy(item->fname, fname);
296    jcr->file_list->insert(item->fname, item); 
297 #endif
298
299 // Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
300    return ret;
301 }
302
303 /*
304  * This function is called for each file seen in fileset.
305  * We check in file_list hash if fname have been backuped
306  * the last time. After we can compare Lstat field. 
307  * Full Lstat usage have been removed on 6612 
308  */
309 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
310 {
311    bool stat = false;
312    char *fname;
313    CurFile elt;
314
315    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
316       return true;
317    }
318
319    strip_path(ff_pkt);
320  
321    if (S_ISDIR(ff_pkt->statp.st_mode)) {
322       fname = ff_pkt->link;
323    } else {
324       fname = ff_pkt->fname;
325    } 
326
327    if (!accurate_lookup(jcr, fname, &elt)) {
328       Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
329       stat = true;
330       goto bail_out;
331    }
332
333    if (elt.seen) { /* file has been seen ? */
334       Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
335       goto bail_out;
336    }
337
338    if (elt.mtime != ff_pkt->statp.st_mtime) {
339      Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
340      stat = true;
341    } else if (elt.ctime != ff_pkt->statp.st_ctime) {
342      Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
343      stat = true;
344    }
345
346    accurate_mark_file_as_seen(jcr, &elt);
347    Dmsg2(dbglvl, "accurate %s = %i\n", fname, stat);
348
349 bail_out:
350    unstrip_path(ff_pkt);
351    return stat;
352 }
353
354 /* 
355  * TODO: use bigbuffer from htable
356  */
357 int accurate_cmd(JCR *jcr)
358 {
359    BSOCK *dir = jcr->dir_bsock;
360    int len;
361    int32_t nb;
362
363    if (!jcr->accurate || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
364       return true;
365    }
366
367    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
368       dir->fsend(_("2991 Bad accurate command\n"));
369       return false;
370    }
371
372    accurate_init(jcr, nb);
373
374    /*
375     * buffer = sizeof(CurFile) + dirmsg
376     * dirmsg = fname + \0 + lstat
377     */
378    /* get current files */
379    while (dir->recv() >= 0) {
380       len = strlen(dir->msg) + 1;
381       if (len < dir->msglen) {
382          accurate_add_file(jcr, dir->msg, dir->msg + len);
383       }
384    }
385
386 #ifdef DEBUG
387    extern void *start_heap;
388
389    char b1[50], b2[50], b3[50], b4[50], b5[50];
390    Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
391          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
392          edit_uint64_with_commas(sm_bytes, b2),
393          edit_uint64_with_commas(sm_max_bytes, b3),
394          edit_uint64_with_commas(sm_buffers, b4),
395          edit_uint64_with_commas(sm_max_buffers, b5));
396
397 #endif
398
399    return true;
400 }
401
402 #ifdef USE_TCADB
403
404 /*
405  * Tokyo Cabinet library doesn't use smartalloc by default
406  * results need to be released with real free()
407  */
408 #undef free
409 void realfree(void *p)
410 {
411    free(p);
412 }
413
414 #endif