]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
fix segfault
[bacula/bacula] / bacula / src / filed / accurate.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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 Kern Sibbald.
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=100;
37
38 typedef struct PrivateCurFile {
39    hlink link;
40    char *fname;
41    char *lstat;
42    bool seen;
43 } CurFile;
44
45 bool accurate_mark_file_as_seen(JCR *jcr, char *fname)
46 {
47    if (!jcr->accurate || !jcr->file_list) {
48       return false;
49    }
50    /* TODO: just use elt->seen = 1 */
51    CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
52    if (temp) {
53       temp->seen = 1;              /* records are in memory */
54       Dmsg1(dbglvl, "marked <%s> as seen\n", fname);
55    } else {
56       Dmsg1(dbglvl, "<%s> not found to be marked as seen\n", fname);
57    }
58    return true;
59 }
60
61 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
62 {
63    /* TODO: just use elt->seen = 1 */
64    CurFile *temp = (CurFile *)jcr->file_list->lookup(elt->fname);
65    if (temp) {
66       temp->seen = 1;              /* records are in memory */
67    }
68    return true;
69 }
70
71 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
72 {
73    bool found=false;
74    ret->seen = 0;
75
76    CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
77    if (temp) {
78       memcpy(ret, temp, sizeof(CurFile));
79       found=true;
80       Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
81    }
82
83    return found;
84 }
85
86 static bool accurate_init(JCR *jcr, int nbfile)
87 {
88    CurFile *elt = NULL;
89    jcr->file_list = (htable *)malloc(sizeof(htable));
90    jcr->file_list->init(elt, &elt->link, nbfile);
91    return true;
92 }
93
94 static bool accurate_send_base_file_list(JCR *jcr)
95 {
96    CurFile *elt;
97    struct stat statc;
98    int32_t LinkFIc;
99    FF_PKT *ff_pkt;
100    int stream = STREAM_UNIX_ATTRIBUTES;
101
102    if (!jcr->accurate || jcr->get_JobLevel() != L_FULL) {
103       return true;
104    }
105
106    if (jcr->file_list == NULL) {
107       return true;
108    }
109
110    ff_pkt = init_find_files();
111    ff_pkt->type = FT_BASE;
112
113    foreach_htable(elt, jcr->file_list) {
114       if (elt->seen) {
115          Dmsg2(dbglvl, "base file fname=%s seen=%i\n", elt->fname, elt->seen);
116          decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */      
117          ff_pkt->fname = elt->fname;
118          ff_pkt->statp = statc;
119          encode_and_send_attributes(jcr, ff_pkt, stream);
120 //       free(elt->fname);
121       }
122    }
123
124    term_find_files(ff_pkt);
125    return true;
126 }
127
128
129 /* This function is called at the end of backup
130  * We walk over all hash disk element, and we check
131  * for elt.seen.
132  */
133 static bool accurate_send_deleted_list(JCR *jcr)
134 {
135    CurFile *elt;
136    struct stat statc;
137    int32_t LinkFIc;
138    FF_PKT *ff_pkt;
139    int stream = STREAM_UNIX_ATTRIBUTES;
140
141    if (!jcr->accurate) {
142       return true;
143    }
144
145    if (jcr->file_list == NULL) {
146       return true;
147    }
148
149    ff_pkt = init_find_files();
150    ff_pkt->type = FT_DELETED;
151
152    foreach_htable(elt, jcr->file_list) {
153       if (elt->seen || plugin_check_file(jcr, elt->fname)) {
154          continue;
155       }
156       Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
157       decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
158       ff_pkt->fname = elt->fname;
159       ff_pkt->statp.st_mtime = statc.st_mtime;
160       ff_pkt->statp.st_ctime = statc.st_ctime;
161       encode_and_send_attributes(jcr, ff_pkt, stream);
162 //    free(elt->fname);
163    }
164
165    term_find_files(ff_pkt);
166    return true;
167 }
168
169 void accurate_free(JCR *jcr)
170 {
171    if (jcr->file_list) {
172       jcr->file_list->destroy();
173       free(jcr->file_list);
174       jcr->file_list = NULL;
175    }
176 }
177
178 /* Send the deleted or the base file list and cleanup  */
179 bool accurate_finish(JCR *jcr)
180 {
181    bool ret=true;
182    if (jcr->accurate) {
183       if (jcr->get_JobLevel() == L_FULL) {
184          ret = accurate_send_base_file_list(jcr);
185       } else {
186          ret = accurate_send_deleted_list(jcr);
187       }
188       
189       accurate_free(jcr);
190    } 
191    return ret;
192 }
193
194 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
195 {
196    bool ret = true;
197    CurFile elt;
198    elt.seen = 0;
199
200    CurFile *item;
201    /* we store CurFile, fname and ctime/mtime in the same chunk */
202    item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+strlen(lstat)+2);
203    memcpy(item, &elt, sizeof(CurFile));
204    item->fname  = (char *)item+sizeof(CurFile);
205    strcpy(item->fname, fname);
206    item->lstat  = item->fname+strlen(item->fname)+1;
207    strcpy(item->lstat, lstat);
208    jcr->file_list->insert(item->fname, item); 
209
210    Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
211    return ret;
212 }
213
214 /*
215  * This function is called for each file seen in fileset.
216  * We check in file_list hash if fname have been backuped
217  * the last time. After we can compare Lstat field. 
218  * Full Lstat usage have been removed on 6612 
219  *
220  * Returns: true   if file has changed (must be backed up)
221  *          false  file not changed
222  */
223 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
224 {
225    struct stat statc;
226    int32_t LinkFIc;
227    bool stat = false;
228    char *fname;
229    CurFile elt;
230
231    if (!jcr->accurate) {
232       return true;
233    }
234
235    strip_path(ff_pkt);
236  
237    if (S_ISDIR(ff_pkt->statp.st_mode)) {
238       fname = ff_pkt->link;
239    } else {
240       fname = ff_pkt->fname;
241    } 
242
243    if (!accurate_lookup(jcr, fname, &elt)) {
244       Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
245       stat = true;
246       goto bail_out;
247    }
248
249    if (elt.seen) { /* file has been seen ? */
250       Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
251       goto bail_out;
252    }
253
254    decode_stat(elt.lstat, &statc, &LinkFIc); /* decode catalog stat */
255
256 #if 0
257    /*
258     * Loop over options supplied by user and verify the
259     * fields he requests.
260     */
261    for (p=Opts_Digest; *p; p++) {
262       char ed1[30], ed2[30];
263       switch (*p) {
264       case 'i':                /* compare INODEs */
265          if (statc.st_ino != statf.st_ino) {
266             prt_fname(jcr);
267             Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
268                  edit_uint64((uint64_t)statc.st_ino, ed1),
269                  edit_uint64((uint64_t)statf.st_ino, ed2));
270             set_jcr_job_status(jcr, JS_Differences);
271          }
272          break;
273       case 'p':                /* permissions bits */
274          if (statc.st_mode != statf.st_mode) {
275             prt_fname(jcr);
276             Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
277                  (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
278             set_jcr_job_status(jcr, JS_Differences);
279          }
280          break;
281       case 'n':                /* number of links */
282          if (statc.st_nlink != statf.st_nlink) {
283             prt_fname(jcr);
284             Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
285                  (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
286             set_jcr_job_status(jcr, JS_Differences);
287          }
288          break;
289       case 'u':                /* user id */
290          if (statc.st_uid != statf.st_uid) {
291             prt_fname(jcr);
292             Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
293                  (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
294             set_jcr_job_status(jcr, JS_Differences);
295          }
296          break;
297       case 'g':                /* group id */
298          if (statc.st_gid != statf.st_gid) {
299             prt_fname(jcr);
300             Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
301                  (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
302             set_jcr_job_status(jcr, JS_Differences);
303          }
304          break;
305       case 's':                /* size */
306          if (statc.st_size != statf.st_size) {
307             prt_fname(jcr);
308             Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
309                  edit_uint64((uint64_t)statc.st_size, ed1),
310                  edit_uint64((uint64_t)statf.st_size, ed2));
311             set_jcr_job_status(jcr, JS_Differences);
312          }
313          break;
314       case 'a':                /* access time */
315          if (statc.st_atime != statf.st_atime) {
316             prt_fname(jcr);
317             Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
318             set_jcr_job_status(jcr, JS_Differences);
319          }
320          break;
321       case 'm':
322          if (statc.st_mtime != statf.st_mtime) {
323             prt_fname(jcr);
324             Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
325             set_jcr_job_status(jcr, JS_Differences);
326          }
327          break;
328       case 'c':                /* ctime */
329          if (statc.st_ctime != statf.st_ctime) {
330             prt_fname(jcr);
331             Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
332             set_jcr_job_status(jcr, JS_Differences);
333          }
334          break;
335       case 'd':                /* file size decrease */
336          if (statc.st_size > statf.st_size) {
337             prt_fname(jcr);
338             Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
339                  edit_uint64((uint64_t)statc.st_size, ed1),
340                  edit_uint64((uint64_t)statf.st_size, ed2));
341             set_jcr_job_status(jcr, JS_Differences);
342          }
343          break;
344       case '5':                /* compare MD5 */
345          Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
346          do_Digest = CRYPTO_DIGEST_MD5;
347          break;
348       case '1':                 /* compare SHA1 */
349          do_Digest = CRYPTO_DIGEST_SHA1;
350                break;
351       case ':':
352       case 'V':
353       default:
354          break;
355             }
356    }
357 #endif
358    /*
359     * We check only mtime/ctime like with the normal
360     * incremental/differential mode
361     */
362    if (statc.st_mtime != ff_pkt->statp.st_mtime) {
363 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
364       Dmsg3(dbglvl, "%s      st_mtime differs (%lld!=%lld)\n", 
365             fname, statc.st_mtime, (utime_t)ff_pkt->statp.st_mtime);
366      stat = true;
367    } else if (!(ff_pkt->flags & FO_MTIMEONLY) 
368               && (statc.st_ctime != ff_pkt->statp.st_ctime)) {
369 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
370       Dmsg1(dbglvl, "%s      st_ctime differs\n", fname);
371       stat = true;
372
373    } else if (statc.st_size != ff_pkt->statp.st_size) {
374 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_size differs\n"), fname);
375       Dmsg1(dbglvl, "%s      st_size differs\n", fname);
376       stat = true;
377    }
378
379    accurate_mark_file_as_seen(jcr, &elt);
380 //   Dmsg2(dbglvl, "accurate %s = %d\n", fname, stat);
381
382 bail_out:
383    unstrip_path(ff_pkt);
384    return stat;
385 }
386
387 /* 
388  * TODO: use big buffer from htable
389  */
390 int accurate_cmd(JCR *jcr)
391 {
392    BSOCK *dir = jcr->dir_bsock;
393    int len;
394    int32_t nb;
395
396    if (job_canceled(jcr)) {
397       return true;
398    }
399    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
400       dir->fsend(_("2991 Bad accurate command\n"));
401       return false;
402    }
403
404    jcr->accurate = true;
405
406    accurate_init(jcr, nb);
407
408    /*
409     * buffer = sizeof(CurFile) + dirmsg
410     * dirmsg = fname + \0 + lstat
411     */
412    /* get current files */
413    while (dir->recv() >= 0) {
414       len = strlen(dir->msg) + 1;
415       if (len < dir->msglen) {
416          accurate_add_file(jcr, dir->msg, dir->msg + len);
417       }
418    }
419
420 #ifdef DEBUG
421    extern void *start_heap;
422
423    char b1[50], b2[50], b3[50], b4[50], b5[50];
424    Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
425          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
426          edit_uint64_with_commas(sm_bytes, b2),
427          edit_uint64_with_commas(sm_max_bytes, b3),
428          edit_uint64_with_commas(sm_buffers, b4),
429          edit_uint64_with_commas(sm_max_buffers, b5));
430 #endif
431
432    return true;
433 }