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