]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
tweak debug
[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 (char *p=ff_pkt->AccurateOpts; *p; p++) {
262       char ed1[30], ed2[30];
263       switch (*p) {
264       case 'i':                /* compare INODEs */
265          if (statc.st_ino != ff_pkt->statp.st_ino) {
266             Dmsg3(dbglvl-1, "%s      st_ino   differ. Cat: %s File: %s\n",
267                   fname,
268                   edit_uint64((uint64_t)statc.st_ino, ed1),
269                   edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
270             stat = true;
271          }
272          break;
273       case 'p':                /* permissions bits */
274          if (statc.st_mode != ff_pkt->statp.st_mode) {
275             Dmsg3(dbglvl-1, "%s     st_mode  differ. Cat: %x File: %x\n",
276                   fname,
277                   (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
278             stat = true;
279          }
280          break;
281       case 'n':                /* number of links */
282          if (statc.st_nlink != ff_pkt->statp.st_nlink) {
283             Dmsg3(dbglvl-1, "%s      st_nlink differ. Cat: %d File: %d\n",
284                   fname,
285                   (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
286             stat = true;
287          }
288          break;
289       case 'u':                /* user id */
290          if (statc.st_uid != ff_pkt->statp.st_uid) {
291             Dmsg3(dbglvl-1, "%s      st_uid   differ. Cat: %u File: %u\n",
292                   fname,
293                   (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
294             stat = true;
295          }
296          break;
297       case 'g':                /* group id */
298          if (statc.st_gid != ff_pkt->statp.st_gid) {
299             Dmsg3(dbglvl-1, "%s      st_gid   differ. Cat: %u File: %u\n",
300                   fname,
301                   (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
302             stat = true;
303          }
304          break;
305       case 's':                /* size */
306          if (statc.st_size != ff_pkt->statp.st_size) {
307             Dmsg3(dbglvl-1, "%s      st_size  differ. Cat: %s File: %s\n",
308                   fname,
309                   edit_uint64((uint64_t)statc.st_size, ed1),
310                   edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
311             stat = true;
312          }
313          break;
314       case 'a':                /* access time */
315          if (statc.st_atime != ff_pkt->statp.st_atime) {
316             Dmsg1(dbglvl-1, "%s      st_atime differs\n", fname);
317             stat = true;
318          }
319          break;
320       case 'm':
321          if (statc.st_mtime != ff_pkt->statp.st_mtime) {
322             Dmsg1(dbglvl-1, "%s      st_mtime differs\n", fname);
323             stat = true;
324          }
325          break;
326       case 'c':                /* ctime */
327          if (statc.st_ctime != ff_pkt->statp.st_ctime) {
328             Dmsg1(dbglvl-1, "      st_ctime differs\n", fname);
329             stat = true;
330          }
331          break;
332       case 'd':                /* file size decrease */
333          if (statc.st_size > ff_pkt->statp.st_size) {
334             Dmsg3(dbglvl-1, "%s      st_size  decrease. Cat: %s File: %s\n",
335                   fname,
336                   edit_uint64((uint64_t)statc.st_size, ed1),
337                   edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
338             stat = true;
339          }
340          break;
341       case '5':                /* compare MD5 */
342          break;
343       case '1':                 /* compare SHA1 */
344          break;
345       case ':':
346       case 'C':
347       default:
348          break;
349             }
350    }
351 //#endif
352 #if 0
353    /*
354     * We check only mtime/ctime like with the normal
355     * incremental/differential mode
356     */
357    if (statc.st_mtime != ff_pkt->statp.st_mtime) {
358 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
359       Dmsg3(dbglvl, "%s      st_mtime differs (%lld!=%lld)\n", 
360             fname, statc.st_mtime, (utime_t)ff_pkt->statp.st_mtime);
361      stat = true;
362    } else if (!(ff_pkt->flags & FO_MTIMEONLY) 
363               && (statc.st_ctime != ff_pkt->statp.st_ctime)) {
364 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
365       Dmsg1(dbglvl, "%s      st_ctime differs\n", fname);
366       stat = true;
367
368    } else if (statc.st_size != ff_pkt->statp.st_size) {
369 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_size differs\n"), fname);
370       Dmsg1(dbglvl, "%s      st_size differs\n", fname);
371       stat = true;
372    }
373 #endif
374
375    accurate_mark_file_as_seen(jcr, &elt);
376 //   Dmsg2(dbglvl, "accurate %s = %d\n", fname, stat);
377
378 bail_out:
379    unstrip_path(ff_pkt);
380    return stat;
381 }
382
383 /* 
384  * TODO: use big buffer from htable
385  */
386 int accurate_cmd(JCR *jcr)
387 {
388    BSOCK *dir = jcr->dir_bsock;
389    int len;
390    int32_t nb;
391
392    if (job_canceled(jcr)) {
393       return true;
394    }
395    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
396       dir->fsend(_("2991 Bad accurate command\n"));
397       return false;
398    }
399
400    jcr->accurate = true;
401
402    accurate_init(jcr, nb);
403
404    /*
405     * buffer = sizeof(CurFile) + dirmsg
406     * dirmsg = fname + \0 + lstat
407     */
408    /* get current files */
409    while (dir->recv() >= 0) {
410       len = strlen(dir->msg) + 1;
411       if (len < dir->msglen) {
412          accurate_add_file(jcr, dir->msg, dir->msg + len);
413       }
414    }
415
416 #ifdef DEBUG
417    extern void *start_heap;
418
419    char b1[50], b2[50], b3[50], b4[50], b5[50];
420    Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
421          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
422          edit_uint64_with_commas(sm_bytes, b2),
423          edit_uint64_with_commas(sm_max_bytes, b3),
424          edit_uint64_with_commas(sm_buffers, b4),
425          edit_uint64_with_commas(sm_max_buffers, b5));
426 #endif
427
428    return true;
429 }