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