]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
Merge branch 'master' into basejobv3
[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          /* TODO: skip the decode and use directly the lstat field */
117          decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */  
118          ff_pkt->fname = elt->fname;
119          ff_pkt->statp = statc;
120          encode_and_send_attributes(jcr, ff_pkt, stream);
121 //       free(elt->fname);
122       }
123    }
124
125    term_find_files(ff_pkt);
126    return true;
127 }
128
129
130 /* This function is called at the end of backup
131  * We walk over all hash disk element, and we check
132  * for elt.seen.
133  */
134 static bool accurate_send_deleted_list(JCR *jcr)
135 {
136    CurFile *elt;
137    struct stat statc;
138    int32_t LinkFIc;
139    FF_PKT *ff_pkt;
140    int stream = STREAM_UNIX_ATTRIBUTES;
141
142    if (!jcr->accurate) {
143       return true;
144    }
145
146    if (jcr->file_list == NULL) {
147       return true;
148    }
149
150    ff_pkt = init_find_files();
151    ff_pkt->type = FT_DELETED;
152
153    foreach_htable(elt, jcr->file_list) {
154       if (elt->seen || plugin_check_file(jcr, elt->fname)) {
155          continue;
156       }
157       Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
158       /* TODO: skip the decode and use directly the lstat field */
159       decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
160       ff_pkt->fname = elt->fname;
161       ff_pkt->statp.st_mtime = statc.st_mtime;
162       ff_pkt->statp.st_ctime = statc.st_ctime;
163       encode_and_send_attributes(jcr, ff_pkt, stream);
164 //    free(elt->fname);
165    }
166
167    term_find_files(ff_pkt);
168    return true;
169 }
170
171 void accurate_free(JCR *jcr)
172 {
173    if (jcr->file_list) {
174       jcr->file_list->destroy();
175       free(jcr->file_list);
176       jcr->file_list = NULL;
177    }
178 }
179
180 /* Send the deleted or the base file list and cleanup  */
181 bool accurate_finish(JCR *jcr)
182 {
183    bool ret=true;
184    if (jcr->accurate) {
185       if (jcr->get_JobLevel() == L_FULL) {
186          ret = accurate_send_base_file_list(jcr);
187       } else {
188          ret = accurate_send_deleted_list(jcr);
189       }
190       
191       accurate_free(jcr);
192       if (jcr->get_JobLevel() == L_FULL) {
193          Dmsg1(0, "Space saved with Base jobs: %lld MB\n", 
194                jcr->base_size/(1024*1024));
195       }
196    }
197    return ret;
198 }
199
200 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
201 {
202    bool ret = true;
203    CurFile elt;
204    elt.seen = 0;
205
206    CurFile *item;
207    /* we store CurFile, fname and ctime/mtime in the same chunk */
208    item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+strlen(lstat)+2);
209    memcpy(item, &elt, sizeof(CurFile));
210    item->fname  = (char *)item+sizeof(CurFile);
211    strcpy(item->fname, fname);
212    item->lstat  = item->fname+strlen(item->fname)+1;
213    strcpy(item->lstat, lstat);
214    jcr->file_list->insert(item->fname, item); 
215
216    Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
217    return ret;
218 }
219
220 /*
221  * This function is called for each file seen in fileset.
222  * We check in file_list hash if fname have been backuped
223  * the last time. After we can compare Lstat field. 
224  * Full Lstat usage have been removed on 6612 
225  *
226  * Returns: true   if file has changed (must be backed up)
227  *          false  file not changed
228  */
229 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
230 {
231    struct stat statc;
232    int32_t LinkFIc;
233    bool stat = false;
234    char *fname;
235    CurFile elt;
236
237    if (!jcr->accurate) {
238       return true;
239    }
240
241    strip_path(ff_pkt);
242  
243    if (S_ISDIR(ff_pkt->statp.st_mode)) {
244       fname = ff_pkt->link;
245    } else {
246       fname = ff_pkt->fname;
247    } 
248
249    if (!accurate_lookup(jcr, fname, &elt)) {
250       Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
251       stat = true;
252       goto bail_out;
253    }
254
255    if (elt.seen) { /* file has been seen ? */
256       Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
257       goto bail_out;
258    }
259
260    decode_stat(elt.lstat, &statc, &LinkFIc); /* decode catalog stat */
261
262 //#if 0
263    /*
264     * Loop over options supplied by user and verify the
265     * fields he requests.
266     */
267    for (char *p=ff_pkt->AccurateOpts; *p; p++) {
268       char ed1[30], ed2[30];
269       switch (*p) {
270       case 'i':                /* compare INODEs */
271          if (statc.st_ino != ff_pkt->statp.st_ino) {
272             Dmsg3(dbglvl-1, "%s      st_ino   differ. Cat: %s File: %s\n",
273                   fname,
274                   edit_uint64((uint64_t)statc.st_ino, ed1),
275                   edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
276             stat = true;
277          }
278          break;
279       case 'p':                /* permissions bits */
280          if (statc.st_mode != ff_pkt->statp.st_mode) {
281             Dmsg3(dbglvl-1, "%s     st_mode  differ. Cat: %x File: %x\n",
282                   fname,
283                   (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
284             stat = true;
285          }
286          break;
287       case 'n':                /* number of links */
288          if (statc.st_nlink != ff_pkt->statp.st_nlink) {
289             Dmsg3(dbglvl-1, "%s      st_nlink differ. Cat: %d File: %d\n",
290                   fname,
291                   (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
292             stat = true;
293          }
294          break;
295       case 'u':                /* user id */
296          if (statc.st_uid != ff_pkt->statp.st_uid) {
297             Dmsg3(dbglvl-1, "%s      st_uid   differ. Cat: %u File: %u\n",
298                   fname,
299                   (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
300             stat = true;
301          }
302          break;
303       case 'g':                /* group id */
304          if (statc.st_gid != ff_pkt->statp.st_gid) {
305             Dmsg3(dbglvl-1, "%s      st_gid   differ. Cat: %u File: %u\n",
306                   fname,
307                   (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
308             stat = true;
309          }
310          break;
311       case 's':                /* size */
312          if (statc.st_size != ff_pkt->statp.st_size) {
313             Dmsg3(dbglvl-1, "%s      st_size  differ. Cat: %s File: %s\n",
314                   fname,
315                   edit_uint64((uint64_t)statc.st_size, ed1),
316                   edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
317             stat = true;
318          }
319          break;
320       case 'a':                /* access time */
321          if (statc.st_atime != ff_pkt->statp.st_atime) {
322             Dmsg1(dbglvl-1, "%s      st_atime differs\n", fname);
323             stat = true;
324          }
325          break;
326       case 'm':
327          if (statc.st_mtime != ff_pkt->statp.st_mtime) {
328             Dmsg1(dbglvl-1, "%s      st_mtime differs\n", fname);
329             stat = true;
330          }
331          break;
332       case 'c':                /* ctime */
333          if (statc.st_ctime != ff_pkt->statp.st_ctime) {
334             Dmsg1(dbglvl-1, "      st_ctime differs\n", fname);
335             stat = true;
336          }
337          break;
338       case 'd':                /* file size decrease */
339          if (statc.st_size > ff_pkt->statp.st_size) {
340             Dmsg3(dbglvl-1, "%s      st_size  decrease. Cat: %s File: %s\n",
341                   fname,
342                   edit_uint64((uint64_t)statc.st_size, ed1),
343                   edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
344             stat = true;
345          }
346          break;
347       case '5':                /* compare MD5 */
348          break;
349       case '1':                 /* compare SHA1 */
350          break;
351       case ':':
352       case 'C':
353       default:
354          break;
355             }
356    }
357 //#endif
358 #if 0
359    /*
360     * We check only mtime/ctime like with the normal
361     * incremental/differential mode
362     */
363    if (statc.st_mtime != ff_pkt->statp.st_mtime) {
364 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
365       Dmsg3(dbglvl, "%s      st_mtime differs (%lld!=%lld)\n", 
366             fname, statc.st_mtime, (utime_t)ff_pkt->statp.st_mtime);
367      stat = true;
368    } else if (!(ff_pkt->flags & FO_MTIMEONLY) 
369               && (statc.st_ctime != ff_pkt->statp.st_ctime)) {
370 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
371       Dmsg1(dbglvl, "%s      st_ctime differs\n", fname);
372       stat = true;
373
374    } else if (statc.st_size != ff_pkt->statp.st_size) {
375 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_size differs\n"), fname);
376       Dmsg1(dbglvl, "%s      st_size differs\n", fname);
377       stat = true;
378    }
379 #endif
380
381    /* In Incr/Diff accurate mode, we mark all files as seen
382     * When in Full+Base mode, we mark only if the file match exactly
383     */
384    if (jcr->get_JobLevel() == L_FULL) {
385       if (!stat) {               
386          /* compute space saved with basefile */
387          jcr->base_size += ff_pkt->statp.st_size;
388          accurate_mark_file_as_seen(jcr, &elt);
389       }
390    } else {
391       accurate_mark_file_as_seen(jcr, &elt);
392    }
393
394 bail_out:
395    unstrip_path(ff_pkt);
396    return stat;
397 }
398
399 /* 
400  * TODO: use big buffer from htable
401  */
402 int accurate_cmd(JCR *jcr)
403 {
404    BSOCK *dir = jcr->dir_bsock;
405    int len;
406    int32_t nb;
407
408    if (job_canceled(jcr)) {
409       return true;
410    }
411    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
412       dir->fsend(_("2991 Bad accurate command\n"));
413       return false;
414    }
415
416    jcr->accurate = true;
417
418    accurate_init(jcr, nb);
419
420    /*
421     * buffer = sizeof(CurFile) + dirmsg
422     * dirmsg = fname + \0 + lstat
423     */
424    /* get current files */
425    while (dir->recv() >= 0) {
426       len = strlen(dir->msg) + 1;
427       if (len < dir->msglen) {
428          accurate_add_file(jcr, dir->msg, dir->msg + len);
429       }
430    }
431
432 #ifdef DEBUG
433    extern void *start_heap;
434
435    char b1[50], b2[50], b3[50], b4[50], b5[50];
436    Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
437          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
438          edit_uint64_with_commas(sm_bytes, b2),
439          edit_uint64_with_commas(sm_max_bytes, b3),
440          edit_uint64_with_commas(sm_buffers, b4),
441          edit_uint64_with_commas(sm_max_buffers, b5));
442 #endif
443
444    return true;
445 }