]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
ebl Make accurate mode with hash disk pass regression tests
[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 typedef struct PrivateCurFile {
37 #ifndef USE_TCHDB
38    hlink link;
39 #endif
40    char *fname;                 /* not stored with tchdb mode */
41    time_t ctime;
42    time_t mtime;
43    bool seen;
44 } CurFile;
45
46 bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
47 {
48    CurFile elt;
49    struct stat statp;
50    int LinkFIc;
51    decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
52    elt.ctime = statp.st_ctime;
53    elt.mtime = statp.st_mtime;
54    elt.seen = 0;
55
56 #ifdef USE_TCHDB
57    if (!tchdbputasync(jcr->file_list,
58                       fname, strlen(fname)+1,
59                       &elt, sizeof(CurFile)))
60    {
61       Dmsg1(2, "Can't add <%s> to file_list\n", fname);
62       /* TODO: check error */
63    }
64 #else  /* HTABLE */
65    CurFile *item;
66    /* we store CurFile, fname and ctime/mtime in the same chunk */
67    item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+1);
68    memcpy(item, &elt, sizeof(CurFile));
69    item->fname  = (char *)item+sizeof(CurFile);
70    strcpy(item->fname, fname);
71    jcr->file_list->insert(item->fname, item); 
72 #endif
73
74    Dmsg2(2, "add fname=<%s> lstat=%s\n", fname, lstat);
75    return true;
76 }
77
78 bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
79 {
80    bool ret=true;
81    CurFile deb;
82
83 #ifdef USE_TCHDB
84    elt->seen = 1;
85    if (!tchdbputasync(jcr->file_list, 
86                       elt->fname, strlen(elt->fname)+1, 
87                       elt, sizeof(CurFile)))
88    {
89       Dmsg1(2, "can't update <%s>\n", elt->fname);
90       ret = false;              /* TODO: add error message */
91    }
92
93 #else  /* HTABLE */
94    CurFile *temp = (CurFile *)jcr->file_list->lookup(elt->fname);
95    temp->seen = 1;
96 #endif    
97    return ret;
98 }
99
100 bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
101 {
102    bool found=false;
103    ret->seen = 0;
104
105 #ifdef USE_TCHDB
106    if (tchdbget3(jcr->file_list, 
107                  fname, strlen(fname)+1, 
108                  ret, sizeof(CurFile)) != -1)
109    {
110       found = true;
111       ret->fname = fname;
112    }
113
114 #else  /* HTABLE */
115    CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
116    if (temp) {
117       memcpy(ret, temp, sizeof(CurFile));
118       found=true;
119    }
120 #endif
121
122    if (found) {
123       Dmsg1(2, "lookup <%s> ok\n", fname);
124    }
125
126    return found;
127 }
128
129 bool accurate_init(JCR *jcr, int nbfile)
130 {
131 #ifdef USE_TCHDB
132    jcr->file_list = tchdbnew();
133    tchdbsetcache(jcr->file_list, 300000);
134    tchdbtune(jcr->file_list,
135              nbfile,            /* nb bucket 0.5n to 4n */
136              7,                 /* size of element 2^x */
137              16,
138              0);                /* options like compression */
139    /* TODO: make accurate file unique */
140    POOL_MEM buf;
141    Mmsg(buf, "/tmp/casket.hdb.%i", jcr->JobId);
142    if(!tchdbopen(jcr->file_list, buf.c_str(), HDBOWRITER | HDBOCREAT)){
143       /* TODO: handle error creation */
144       //ecode = tchdbecode(hdb);
145       //fprintf(stderr, "open error: %s\n", tchdberrmsg(ecode));
146    }
147
148 #else  /* HTABLE */
149    CurFile *elt=NULL;
150    jcr->file_list = (htable *)malloc(sizeof(htable));
151    jcr->file_list->init(elt, &elt->link, nbfile);
152 #endif
153
154    return true;
155 }
156
157 /*
158  * This function is called for each file seen in fileset.
159  * We check in file_list hash if fname have been backuped
160  * the last time. After we can compare Lstat field. 
161  * Full Lstat usage have been removed on 6612 
162  */
163 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
164 {
165    bool stat = false;
166    char *fname;
167    CurFile elt;
168
169    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
170       return true;
171    }
172
173    strip_path(ff_pkt);
174  
175    if (S_ISDIR(ff_pkt->statp.st_mode)) {
176       fname = ff_pkt->link;
177    } else {
178       fname = ff_pkt->fname;
179    } 
180
181    if (!accurate_lookup(jcr, fname, &elt)) {
182       Dmsg1(2, "accurate %s (not found)\n", fname);
183       stat = true;
184       goto bail_out;
185    }
186
187    if (elt.seen) { /* file has been seen ? */
188       Dmsg1(2, "accurate %s (already seen)\n", fname);
189       goto bail_out;
190    }
191
192    if (elt.mtime != ff_pkt->statp.st_mtime) {
193      Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
194      stat = true;
195    } else if (elt.ctime != ff_pkt->statp.st_ctime) {
196      Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
197      stat = true;
198    }
199
200    if (stat) {
201       Dmsg4(1, "%i = %i\t%i = %i\n", elt.mtime, ff_pkt->statp.st_mtime,
202             elt.ctime, ff_pkt->statp.st_ctime);
203    }
204
205    accurate_mark_file_as_seen(jcr, &elt);
206    Dmsg2(2, "accurate %s = %i\n", fname, stat);
207
208 bail_out:
209    unstrip_path(ff_pkt);
210    return stat;
211 }
212
213 /* 
214  * TODO: use bigbuffer from htable
215  */
216 int accurate_cmd(JCR *jcr)
217 {
218    BSOCK *dir = jcr->dir_bsock;
219    int len;
220    int32_t nb;
221
222    if (!jcr->accurate || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
223       return true;
224    }
225
226    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
227       dir->fsend(_("2991 Bad accurate command\n"));
228       return false;
229    }
230    Dmsg2(2, "nb=%d msg=%s\n", nb, dir->msg);
231
232    accurate_init(jcr, nb);
233
234    /*
235     * buffer = sizeof(CurFile) + dirmsg
236     * dirmsg = fname + \0 + lstat
237     */
238    /* get current files */
239    while (dir->recv() >= 0) {
240       Dmsg1(2, "accurate_cmd fname=%s\n", dir->msg);
241       len = strlen(dir->msg) + 1;
242       if (len < dir->msglen) {
243          accurate_add_file(jcr, dir->msg, dir->msg + len);
244       }
245    }
246
247 #ifdef DEBUG
248    extern void *start_heap;
249
250    char b1[50], b2[50], b3[50], b4[50], b5[50];
251    Dmsg5(1," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
252          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
253          edit_uint64_with_commas(sm_bytes, b2),
254          edit_uint64_with_commas(sm_max_bytes, b3),
255          edit_uint64_with_commas(sm_buffers, b4),
256          edit_uint64_with_commas(sm_max_buffers, b5));
257
258 #endif
259
260    return true;
261 }
262
263 bool accurate_send_deleted_list(JCR *jcr)
264 {
265    CurFile *elt;
266    FF_PKT *ff_pkt;
267    int stream = STREAM_UNIX_ATTRIBUTES;
268
269    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
270       goto bail_out;
271    }
272
273    if (jcr->file_list == NULL) {
274       goto bail_out;
275    }
276
277    ff_pkt = init_find_files();
278    ff_pkt->type = FT_DELETED;
279
280 #ifdef USE_TCHDB
281    char *key;
282    CurFile item;
283    elt = &item;
284   /* traverse records */
285    tchdbiterinit(jcr->file_list);
286    while((key = tchdbiternext2(jcr->file_list)) != NULL) {
287       if (tchdbget3(jcr->file_list, 
288                     key, strlen(key)+1, 
289                     elt, sizeof(CurFile)) != -1)
290       {
291          if (!elt->seen) {
292             ff_pkt->fname = key;
293             ff_pkt->statp.st_mtime = elt->mtime;
294             ff_pkt->statp.st_ctime = elt->ctime;
295             encode_and_send_attributes(jcr, ff_pkt, stream);
296             Dmsg1(2, "deleted <%s>\n", key);
297          }
298  //      free(key);
299
300       } else {                  /* TODO: add error message */
301          Dmsg1(2, "No value for <%s> key\n", key);
302       }
303    }
304 #else
305    foreach_htable (elt, jcr->file_list) {
306       if (!elt->seen) { /* already seen */
307          Dmsg2(1, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
308          ff_pkt->fname = elt->fname;
309          ff_pkt->statp.st_mtime = elt->mtime;
310          ff_pkt->statp.st_ctime = elt->ctime;
311          encode_and_send_attributes(jcr, ff_pkt, stream);
312       }
313 //      free(elt->fname);
314    }
315 #endif
316
317    term_find_files(ff_pkt);
318 bail_out:
319    /* TODO: clean htable when this function is not reached ? */
320    if (jcr->file_list) {
321 #ifdef USE_TCHDB
322       if(!tchdbclose(jcr->file_list)){
323 //       ecode = tchdbecode(hdb);
324 //       fprintf(stderr, "close error: %s\n", tchdberrmsg(ecode));
325       }
326
327       /* delete the object */
328       tchdbdel(jcr->file_list);
329       POOL_MEM buf;
330       Mmsg(buf, "/tmp/casket.hdb.%i", jcr->JobId);
331 //      unlink("/tmp/casket.hdb");
332 #else
333       jcr->file_list->destroy();
334       free(jcr->file_list);
335 #endif
336       jcr->file_list = NULL;
337    }
338    return true;
339 }