2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
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
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.
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
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.
36 static int dbglvl=100;
38 typedef struct PrivateCurFile {
45 bool accurate_mark_file_as_seen(JCR *jcr, char *fname)
47 if (!jcr->accurate || !jcr->file_list) {
50 /* TODO: just use elt->seen = 1 */
51 CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
53 temp->seen = 1; /* records are in memory */
54 Dmsg1(dbglvl, "marked <%s> as seen\n", fname);
56 Dmsg1(dbglvl, "<%s> not found to be marked as seen\n", fname);
61 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
63 /* TODO: just use elt->seen = 1 */
64 CurFile *temp = (CurFile *)jcr->file_list->lookup(elt->fname);
66 temp->seen = 1; /* records are in memory */
71 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
76 CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
78 memcpy(ret, temp, sizeof(CurFile));
80 Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
86 static bool accurate_init(JCR *jcr, int nbfile)
89 jcr->file_list = (htable *)malloc(sizeof(htable));
90 jcr->file_list->init(elt, &elt->link, nbfile);
94 static bool accurate_send_base_file_list(JCR *jcr)
100 int stream = STREAM_UNIX_ATTRIBUTES;
102 if (!jcr->accurate || jcr->get_JobLevel() != L_FULL) {
106 if (jcr->file_list == NULL) {
110 ff_pkt = init_find_files();
111 ff_pkt->type = FT_BASE;
113 foreach_htable(elt, jcr->file_list) {
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);
125 term_find_files(ff_pkt);
130 /* This function is called at the end of backup
131 * We walk over all hash disk element, and we check
134 static bool accurate_send_deleted_list(JCR *jcr)
140 int stream = STREAM_UNIX_ATTRIBUTES;
142 if (!jcr->accurate) {
146 if (jcr->file_list == NULL) {
150 ff_pkt = init_find_files();
151 ff_pkt->type = FT_DELETED;
153 foreach_htable(elt, jcr->file_list) {
154 if (elt->seen || plugin_check_file(jcr, elt->fname)) {
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);
167 term_find_files(ff_pkt);
171 void accurate_free(JCR *jcr)
173 if (jcr->file_list) {
174 jcr->file_list->destroy();
175 free(jcr->file_list);
176 jcr->file_list = NULL;
180 /* Send the deleted or the base file list and cleanup */
181 bool accurate_finish(JCR *jcr)
185 if (jcr->get_JobLevel() == L_FULL) {
186 ret = accurate_send_base_file_list(jcr);
188 ret = accurate_send_deleted_list(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));
200 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
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);
216 Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
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
226 * Returns: true if file has changed (must be backed up)
227 * false file not changed
229 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
237 if (!jcr->accurate) {
243 if (S_ISDIR(ff_pkt->statp.st_mode)) {
244 fname = ff_pkt->link;
246 fname = ff_pkt->fname;
249 if (!accurate_lookup(jcr, fname, &elt)) {
250 Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
255 if (elt.seen) { /* file has been seen ? */
256 Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
260 decode_stat(elt.lstat, &statc, &LinkFIc); /* decode catalog stat */
264 * Loop over options supplied by user and verify the
265 * fields he requests.
267 for (char *p=ff_pkt->AccurateOpts; *p; p++) {
268 char ed1[30], ed2[30];
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",
274 edit_uint64((uint64_t)statc.st_ino, ed1),
275 edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
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",
283 (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
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",
291 (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
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",
299 (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
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",
307 (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
312 if (statc.st_size != ff_pkt->statp.st_size) {
313 Dmsg3(dbglvl-1, "%s st_size differ. Cat: %s File: %s\n",
315 edit_uint64((uint64_t)statc.st_size, ed1),
316 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
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);
327 if (statc.st_mtime != ff_pkt->statp.st_mtime) {
328 Dmsg1(dbglvl-1, "%s st_mtime differs\n", fname);
332 case 'c': /* ctime */
333 if (statc.st_ctime != ff_pkt->statp.st_ctime) {
334 Dmsg1(dbglvl-1, " st_ctime differs\n", fname);
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",
342 edit_uint64((uint64_t)statc.st_size, ed1),
343 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
347 case '5': /* compare MD5 */
349 case '1': /* compare SHA1 */
360 * We check only mtime/ctime like with the normal
361 * incremental/differential mode
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);
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);
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);
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
384 if (jcr->get_JobLevel() == L_FULL) {
386 /* compute space saved with basefile */
387 jcr->base_size += ff_pkt->statp.st_size;
388 accurate_mark_file_as_seen(jcr, &elt);
391 accurate_mark_file_as_seen(jcr, &elt);
395 unstrip_path(ff_pkt);
400 * TODO: use big buffer from htable
402 int accurate_cmd(JCR *jcr)
404 BSOCK *dir = jcr->dir_bsock;
408 if (job_canceled(jcr)) {
411 if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
412 dir->fsend(_("2991 Bad accurate command\n"));
416 jcr->accurate = true;
418 accurate_init(jcr, nb);
421 * buffer = sizeof(CurFile) + dirmsg
422 * dirmsg = fname + \0 + lstat
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);
433 extern void *start_heap;
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));