]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
ba491bfcac7cf0387cfe8c5e29e5a2ee44358a02
[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    char *fname;                 /* not stored with tchdb mode */
40    time_t ctime;
41    time_t mtime;
42    bool seen;
43 } CurFile;
44
45 static void realfree(void *p);  /* used by tokyo code */
46
47 /*
48  * Update hash element seen=1
49  */
50 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
51 {
52    bool ret=true;
53
54    elt->seen = 1;
55    if (!tcadbput(jcr->file_list, 
56                  elt->fname, strlen(elt->fname)+1, 
57                  elt, sizeof(CurFile)))
58    { /* TODO: disabling accurate mode ?  */
59       Jmsg(jcr, M_ERROR, 1, _("Can't update accurate hash disk\n"));
60       ret = false;
61    }
62
63    return ret;
64 }
65
66 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
67 {
68    bool found=false;
69    ret->seen = 0;
70    int size;
71    CurFile *elt;
72
73    elt = (CurFile*)tcadbget(jcr->file_list, 
74                             fname, strlen(fname)+1, &size);
75    if (elt)
76    {
77       /* TODO: don't malloc/free results */
78       found = true;
79       elt->fname = fname;
80       memcpy(ret, elt, sizeof(CurFile));
81       realfree(elt);
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 = tcadbnew();
93 //
94 //   tchdbsetcache(jcr->file_list, 300000);
95 //   tchdbtune(jcr->file_list,
96 //           nbfile,            /* nb bucket 0.5n to 4n */
97 //           6,                 /* size of element 2^x */
98 //           16,
99 //           0);                /* options like compression */
100 //
101    jcr->hash_name  = get_pool_memory(PM_MESSAGE);
102    POOLMEM *temp = get_pool_memory(PM_MESSAGE);
103
104    if (nbfile > 500000) {
105       make_unique_filename(&jcr->hash_name, jcr->JobId, "accurate");
106       pm_strcat(jcr->hash_name, ".tcb");
107       Mmsg(temp, "%s#bnum=%i#mode=e#opts=l",
108            jcr->hash_name, nbfile*4); 
109       Dmsg1(dbglvl, "Doing accurate hash on disk %s\n", jcr->hash_name);
110    } else {
111       Dmsg0(dbglvl, "Doing accurate hash on memory\n");
112       pm_strcpy(jcr->hash_name, "*");
113       pm_strcpy(temp, "*");
114    }
115    
116    if(!tcadbopen(jcr->file_list, jcr->hash_name)){
117       Jmsg(jcr, M_ERROR, 1, _("Can't open accurate hash disk\n"));
118       Jmsg(jcr, M_INFO, 1, _("Disabling accurate mode\n"));
119       tcadbdel(jcr->file_list);
120       jcr->file_list = NULL;
121       jcr->accurate = false;
122    }
123    free_pool_memory(temp);
124    return jcr->file_list != NULL;
125 }
126
127 /* This function is called at the end of backup
128  * We walk over all hash disk element, and we check
129  * for elt.seen.
130  */
131 bool accurate_send_deleted_list(JCR *jcr)
132 {
133    char *key;
134    CurFile *elt;
135    int size;
136    FF_PKT *ff_pkt;
137    int stream = STREAM_UNIX_ATTRIBUTES;
138
139    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
140       goto bail_out;
141    }
142
143    if (jcr->file_list == NULL) {
144       goto bail_out;
145    }
146
147    ff_pkt = init_find_files();
148    ff_pkt->type = FT_DELETED;
149
150    /* traverse records */
151    tcadbiterinit(jcr->file_list);
152    while((key = tcadbiternext2(jcr->file_list)) != NULL) {
153       elt = (CurFile *) tcadbget(jcr->file_list, 
154                                  key, strlen(key)+1, &size);
155       if (elt)
156       {
157          if (!elt->seen) {      /* already seen */
158             ff_pkt->fname = key;
159             ff_pkt->statp.st_mtime = elt->mtime;
160             ff_pkt->statp.st_ctime = elt->ctime;
161             encode_and_send_attributes(jcr, ff_pkt, stream);
162          }
163          realfree(elt);
164       }
165       realfree(key);            /* tokyo cabinet have to use real free() */
166    }
167
168    term_find_files(ff_pkt);
169 bail_out:
170    /* TODO: clean htable when this function is not reached ? */
171    if (jcr->file_list) {
172       if(!tcadbclose(jcr->file_list)){
173          Jmsg(jcr, M_ERROR, 1, _("Can't close accurate hash disk\n"));
174       }
175
176       /* delete the object */
177       tcadbdel(jcr->file_list);
178       if (!bstrcmp(jcr->hash_name, "*")) {
179          unlink(jcr->hash_name);
180       }
181
182       free_pool_memory(jcr->hash_name);
183       jcr->hash_name = NULL;
184       jcr->file_list = NULL;
185    }
186    return true;
187 }
188
189 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
190 {
191    bool ret = true;
192    CurFile elt;
193    struct stat statp;
194    int LinkFIc;
195    decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
196    elt.ctime = statp.st_ctime;
197    elt.mtime = statp.st_mtime;
198    elt.seen = 0;
199
200    if (!tcadbput(jcr->file_list,
201                  fname, strlen(fname)+1,
202                  &elt, sizeof(CurFile)))
203    {
204       Jmsg(jcr, M_ERROR, 1, _("Can't update accurate hash disk ERR=%s\n"));
205       ret = false;
206    }
207
208 // Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
209    return ret;
210 }
211
212 /*
213  * This function is called for each file seen in fileset.
214  * We check in file_list hash if fname have been backuped
215  * the last time. After we can compare Lstat field. 
216  * Full Lstat usage have been removed on 6612 
217  */
218 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
219 {
220    bool stat = false;
221    char *fname;
222    CurFile elt;
223
224    if (!jcr->accurate || jcr->JobLevel == L_FULL) {
225       return true;
226    }
227
228    strip_path(ff_pkt);
229  
230    if (S_ISDIR(ff_pkt->statp.st_mode)) {
231       fname = ff_pkt->link;
232    } else {
233       fname = ff_pkt->fname;
234    } 
235
236    if (!accurate_lookup(jcr, fname, &elt)) {
237       Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
238       stat = true;
239       goto bail_out;
240    }
241
242    if (elt.seen) { /* file has been seen ? */
243       Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
244       goto bail_out;
245    }
246
247    if (elt.mtime != ff_pkt->statp.st_mtime) {
248      Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
249      stat = true;
250    } else if (elt.ctime != ff_pkt->statp.st_ctime) {
251      Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
252      stat = true;
253    }
254
255    accurate_mark_file_as_seen(jcr, &elt);
256    Dmsg2(dbglvl, "accurate %s = %i\n", fname, stat);
257
258 bail_out:
259    unstrip_path(ff_pkt);
260    return stat;
261 }
262
263 /* 
264  * TODO: use bigbuffer from htable
265  */
266 int accurate_cmd(JCR *jcr)
267 {
268    BSOCK *dir = jcr->dir_bsock;
269    int len;
270    int32_t nb;
271
272    if (!jcr->accurate || job_canceled(jcr) || jcr->JobLevel==L_FULL) {
273       return true;
274    }
275
276    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
277       dir->fsend(_("2991 Bad accurate command\n"));
278       return false;
279    }
280
281    accurate_init(jcr, nb);
282
283    /*
284     * buffer = sizeof(CurFile) + dirmsg
285     * dirmsg = fname + \0 + lstat
286     */
287    /* get current files */
288    while (dir->recv() >= 0) {
289       len = strlen(dir->msg) + 1;
290       if (len < dir->msglen) {
291          accurate_add_file(jcr, dir->msg, dir->msg + len);
292       }
293    }
294
295 #ifdef DEBUG
296    extern void *start_heap;
297
298    char b1[50], b2[50], b3[50], b4[50], b5[50];
299    Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
300          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
301          edit_uint64_with_commas(sm_bytes, b2),
302          edit_uint64_with_commas(sm_max_bytes, b3),
303          edit_uint64_with_commas(sm_buffers, b4),
304          edit_uint64_with_commas(sm_max_buffers, b5));
305
306 #endif
307
308    return true;
309 }
310
311 /*
312  * Tokyo Cabinet library doesn't use smartalloc by default
313  * results need to be released with real free()
314  */
315 #undef free
316 void realfree(void *p)
317 {
318    free(p);
319 }
320