]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/accurate.c
work on stats, fix bug with batch connection
[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       if (jcr->get_JobLevel() == L_FULL) {
191          Dmsg1(0, "Space saved with Base jobs: %lld MB\n", 
192                jcr->base_size/(1024*1024));
193       }
194    }
195    return ret;
196 }
197
198 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
199 {
200    bool ret = true;
201    CurFile elt;
202    elt.seen = 0;
203
204    CurFile *item;
205    /* we store CurFile, fname and ctime/mtime in the same chunk */
206    item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+strlen(lstat)+2);
207    memcpy(item, &elt, sizeof(CurFile));
208    item->fname  = (char *)item+sizeof(CurFile);
209    strcpy(item->fname, fname);
210    item->lstat  = item->fname+strlen(item->fname)+1;
211    strcpy(item->lstat, lstat);
212    jcr->file_list->insert(item->fname, item); 
213
214    Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
215    return ret;
216 }
217
218 /*
219  * This function is called for each file seen in fileset.
220  * We check in file_list hash if fname have been backuped
221  * the last time. After we can compare Lstat field. 
222  * Full Lstat usage have been removed on 6612 
223  *
224  * Returns: true   if file has changed (must be backed up)
225  *          false  file not changed
226  */
227 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
228 {
229    struct stat statc;
230    int32_t LinkFIc;
231    bool stat = false;
232    char *fname;
233    CurFile elt;
234
235    if (!jcr->accurate) {
236       return true;
237    }
238
239    strip_path(ff_pkt);
240  
241    if (S_ISDIR(ff_pkt->statp.st_mode)) {
242       fname = ff_pkt->link;
243    } else {
244       fname = ff_pkt->fname;
245    } 
246
247    if (!accurate_lookup(jcr, fname, &elt)) {
248       Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
249       stat = true;
250       goto bail_out;
251    }
252
253    if (elt.seen) { /* file has been seen ? */
254       Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
255       goto bail_out;
256    }
257
258    decode_stat(elt.lstat, &statc, &LinkFIc); /* decode catalog stat */
259
260 //#if 0
261    /*
262     * Loop over options supplied by user and verify the
263     * fields he requests.
264     */
265    for (char *p=ff_pkt->AccurateOpts; *p; p++) {
266       char ed1[30], ed2[30];
267       switch (*p) {
268       case 'i':                /* compare INODEs */
269          if (statc.st_ino != ff_pkt->statp.st_ino) {
270             Dmsg3(dbglvl-1, "%s      st_ino   differ. Cat: %s File: %s\n",
271                   fname,
272                   edit_uint64((uint64_t)statc.st_ino, ed1),
273                   edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
274             stat = true;
275          }
276          break;
277       case 'p':                /* permissions bits */
278          if (statc.st_mode != ff_pkt->statp.st_mode) {
279             Dmsg3(dbglvl-1, "%s     st_mode  differ. Cat: %x File: %x\n",
280                   fname,
281                   (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
282             stat = true;
283          }
284          break;
285       case 'n':                /* number of links */
286          if (statc.st_nlink != ff_pkt->statp.st_nlink) {
287             Dmsg3(dbglvl-1, "%s      st_nlink differ. Cat: %d File: %d\n",
288                   fname,
289                   (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
290             stat = true;
291          }
292          break;
293       case 'u':                /* user id */
294          if (statc.st_uid != ff_pkt->statp.st_uid) {
295             Dmsg3(dbglvl-1, "%s      st_uid   differ. Cat: %u File: %u\n",
296                   fname,
297                   (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
298             stat = true;
299          }
300          break;
301       case 'g':                /* group id */
302          if (statc.st_gid != ff_pkt->statp.st_gid) {
303             Dmsg3(dbglvl-1, "%s      st_gid   differ. Cat: %u File: %u\n",
304                   fname,
305                   (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
306             stat = true;
307          }
308          break;
309       case 's':                /* size */
310          if (statc.st_size != ff_pkt->statp.st_size) {
311             Dmsg3(dbglvl-1, "%s      st_size  differ. Cat: %s File: %s\n",
312                   fname,
313                   edit_uint64((uint64_t)statc.st_size, ed1),
314                   edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
315             stat = true;
316          }
317          break;
318       case 'a':                /* access time */
319          if (statc.st_atime != ff_pkt->statp.st_atime) {
320             Dmsg1(dbglvl-1, "%s      st_atime differs\n", fname);
321             stat = true;
322          }
323          break;
324       case 'm':
325          if (statc.st_mtime != ff_pkt->statp.st_mtime) {
326             Dmsg1(dbglvl-1, "%s      st_mtime differs\n", fname);
327             stat = true;
328          }
329          break;
330       case 'c':                /* ctime */
331          if (statc.st_ctime != ff_pkt->statp.st_ctime) {
332             Dmsg1(dbglvl-1, "      st_ctime differs\n", fname);
333             stat = true;
334          }
335          break;
336       case 'd':                /* file size decrease */
337          if (statc.st_size > ff_pkt->statp.st_size) {
338             Dmsg3(dbglvl-1, "%s      st_size  decrease. Cat: %s File: %s\n",
339                   fname,
340                   edit_uint64((uint64_t)statc.st_size, ed1),
341                   edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
342             stat = true;
343          }
344          break;
345       case '5':                /* compare MD5 */
346          break;
347       case '1':                 /* compare SHA1 */
348          break;
349       case ':':
350       case 'C':
351       default:
352          break;
353             }
354    }
355 //#endif
356 #if 0
357    /*
358     * We check only mtime/ctime like with the normal
359     * incremental/differential mode
360     */
361    if (statc.st_mtime != ff_pkt->statp.st_mtime) {
362 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_mtime differs\n"), fname);
363       Dmsg3(dbglvl, "%s      st_mtime differs (%lld!=%lld)\n", 
364             fname, statc.st_mtime, (utime_t)ff_pkt->statp.st_mtime);
365      stat = true;
366    } else if (!(ff_pkt->flags & FO_MTIMEONLY) 
367               && (statc.st_ctime != ff_pkt->statp.st_ctime)) {
368 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_ctime differs\n"), fname);
369       Dmsg1(dbglvl, "%s      st_ctime differs\n", fname);
370       stat = true;
371
372    } else if (statc.st_size != ff_pkt->statp.st_size) {
373 //   Jmsg(jcr, M_SAVED, 0, _("%s      st_size differs\n"), fname);
374       Dmsg1(dbglvl, "%s      st_size differs\n", fname);
375       stat = true;
376    }
377 #endif
378
379    /* compute space saved with basefile */
380    if (jcr->get_JobLevel() == L_FULL && !stat) {
381       jcr->base_size += ff_pkt->statp.st_size;
382    }
383
384    accurate_mark_file_as_seen(jcr, &elt);
385 //   Dmsg2(dbglvl, "accurate %s = %d\n", fname, stat);
386
387 bail_out:
388    unstrip_path(ff_pkt);
389    return stat;
390 }
391
392 /* 
393  * TODO: use big buffer from htable
394  */
395 int accurate_cmd(JCR *jcr)
396 {
397    BSOCK *dir = jcr->dir_bsock;
398    int len;
399    int32_t nb;
400
401    if (job_canceled(jcr)) {
402       return true;
403    }
404    if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
405       dir->fsend(_("2991 Bad accurate command\n"));
406       return false;
407    }
408
409    jcr->accurate = true;
410
411    accurate_init(jcr, nb);
412
413    /*
414     * buffer = sizeof(CurFile) + dirmsg
415     * dirmsg = fname + \0 + lstat
416     */
417    /* get current files */
418    while (dir->recv() >= 0) {
419       len = strlen(dir->msg) + 1;
420       if (len < dir->msglen) {
421          accurate_add_file(jcr, dir->msg, dir->msg + len);
422       }
423    }
424
425 #ifdef DEBUG
426    extern void *start_heap;
427
428    char b1[50], b2[50], b3[50], b4[50], b5[50];
429    Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
430          edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
431          edit_uint64_with_commas(sm_bytes, b2),
432          edit_uint64_with_commas(sm_max_bytes, b3),
433          edit_uint64_with_commas(sm_buffers, b4),
434          edit_uint64_with_commas(sm_max_buffers, b5));
435 #endif
436
437    return true;
438 }