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 {
41 utime_t ctime; /* can be replaced by struct stat */
46 bool accurate_mark_file_as_seen(JCR *jcr, char *fname)
48 if (!jcr->accurate || !jcr->file_list) {
51 /* TODO: just use elt->seen = 1 */
52 CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
54 temp->seen = 1; /* records are in memory */
55 Dmsg1(dbglvl, "marked <%s> as seen\n", fname);
57 Dmsg1(dbglvl, "<%s> not found to be marked as seen\n", fname);
62 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
64 /* TODO: just use elt->seen = 1 */
65 CurFile *temp = (CurFile *)jcr->file_list->lookup(elt->fname);
67 temp->seen = 1; /* records are in memory */
72 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
77 CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
79 memcpy(ret, temp, sizeof(CurFile));
81 Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
87 static bool accurate_init(JCR *jcr, int nbfile)
90 jcr->file_list = (htable *)malloc(sizeof(htable));
91 jcr->file_list->init(elt, &elt->link, nbfile);
95 static bool accurate_send_base_file_list(JCR *jcr)
99 int stream = STREAM_UNIX_ATTRIBUTES;
101 if (!jcr->accurate || jcr->get_JobLevel() != L_FULL) {
105 if (jcr->file_list == NULL) {
109 ff_pkt = init_find_files();
110 ff_pkt->type = FT_BASE;
112 foreach_htable(elt, jcr->file_list) {
114 Dmsg2(dbglvl, "base file fname=%s seen=%i\n", elt->fname, elt->seen);
115 ff_pkt->fname = elt->fname;
116 ff_pkt->statp.st_mtime = elt->mtime;
117 ff_pkt->statp.st_ctime = elt->ctime;
118 encode_and_send_attributes(jcr, ff_pkt, stream);
123 term_find_files(ff_pkt);
128 /* This function is called at the end of backup
129 * We walk over all hash disk element, and we check
132 static bool accurate_send_deleted_list(JCR *jcr)
136 int stream = STREAM_UNIX_ATTRIBUTES;
138 if (!jcr->accurate) {
142 if (jcr->file_list == NULL) {
146 ff_pkt = init_find_files();
147 ff_pkt->type = FT_DELETED;
149 foreach_htable(elt, jcr->file_list) {
150 if (elt->seen || plugin_check_file(jcr, elt->fname)) {
153 Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
154 ff_pkt->fname = elt->fname;
155 ff_pkt->statp.st_mtime = elt->mtime;
156 ff_pkt->statp.st_ctime = elt->ctime;
157 encode_and_send_attributes(jcr, ff_pkt, stream);
161 term_find_files(ff_pkt);
165 void accurate_free(JCR *jcr)
167 if (jcr->file_list) {
168 jcr->file_list->destroy();
169 free(jcr->file_list);
170 jcr->file_list = NULL;
174 /* Send the deleted or the base file list and cleanup */
175 bool accurate_finish(JCR *jcr)
179 if (jcr->get_JobLevel() == L_FULL) {
180 ret = accurate_send_base_file_list(jcr);
182 ret = accurate_send_deleted_list(jcr);
190 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
196 decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
197 elt.ctime = statp.st_ctime;
198 elt.mtime = statp.st_mtime;
202 /* we store CurFile, fname and ctime/mtime in the same chunk */
203 item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+1);
204 memcpy(item, &elt, sizeof(CurFile));
205 item->fname = (char *)item+sizeof(CurFile);
206 strcpy(item->fname, fname);
207 jcr->file_list->insert(item->fname, item);
209 Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
214 * This function is called for each file seen in fileset.
215 * We check in file_list hash if fname have been backuped
216 * the last time. After we can compare Lstat field.
217 * Full Lstat usage have been removed on 6612
219 * Returns: true if file has changed (must be backed up)
220 * false file not changed
222 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
228 if (!jcr->accurate) {
234 if (S_ISDIR(ff_pkt->statp.st_mode)) {
235 fname = ff_pkt->link;
237 fname = ff_pkt->fname;
240 if (!accurate_lookup(jcr, fname, &elt)) {
241 Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
246 if (elt.seen) { /* file has been seen ? */
247 Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
253 * Loop over options supplied by user and verify the
254 * fields he requests.
256 for (p=Opts_Digest; *p; p++) {
257 char ed1[30], ed2[30];
259 case 'i': /* compare INODEs */
260 if (statc.st_ino != statf.st_ino) {
262 Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
263 edit_uint64((uint64_t)statc.st_ino, ed1),
264 edit_uint64((uint64_t)statf.st_ino, ed2));
265 set_jcr_job_status(jcr, JS_Differences);
268 case 'p': /* permissions bits */
269 if (statc.st_mode != statf.st_mode) {
271 Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
272 (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
273 set_jcr_job_status(jcr, JS_Differences);
276 case 'n': /* number of links */
277 if (statc.st_nlink != statf.st_nlink) {
279 Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
280 (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
281 set_jcr_job_status(jcr, JS_Differences);
284 case 'u': /* user id */
285 if (statc.st_uid != statf.st_uid) {
287 Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
288 (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
289 set_jcr_job_status(jcr, JS_Differences);
292 case 'g': /* group id */
293 if (statc.st_gid != statf.st_gid) {
295 Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
296 (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
297 set_jcr_job_status(jcr, JS_Differences);
301 if (statc.st_size != statf.st_size) {
303 Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
304 edit_uint64((uint64_t)statc.st_size, ed1),
305 edit_uint64((uint64_t)statf.st_size, ed2));
306 set_jcr_job_status(jcr, JS_Differences);
309 case 'a': /* access time */
310 if (statc.st_atime != statf.st_atime) {
312 Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
313 set_jcr_job_status(jcr, JS_Differences);
317 if (statc.st_mtime != statf.st_mtime) {
319 Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
320 set_jcr_job_status(jcr, JS_Differences);
323 case 'c': /* ctime */
324 if (statc.st_ctime != statf.st_ctime) {
326 Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
327 set_jcr_job_status(jcr, JS_Differences);
330 case 'd': /* file size decrease */
331 if (statc.st_size > statf.st_size) {
333 Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
334 edit_uint64((uint64_t)statc.st_size, ed1),
335 edit_uint64((uint64_t)statf.st_size, ed2));
336 set_jcr_job_status(jcr, JS_Differences);
339 case '5': /* compare MD5 */
340 Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
341 do_Digest = CRYPTO_DIGEST_MD5;
343 case '1': /* compare SHA1 */
344 do_Digest = CRYPTO_DIGEST_SHA1;
354 * We check only mtime/ctime like with the normal
355 * incremental/differential mode
357 if (elt.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, elt.mtime, (utime_t)ff_pkt->statp.st_mtime);
362 } else if (!(ff_pkt->flags & FO_MTIMEONLY)
363 && (elt.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);
369 accurate_mark_file_as_seen(jcr, &elt);
370 // Dmsg2(dbglvl, "accurate %s = %d\n", fname, stat);
373 unstrip_path(ff_pkt);
378 * TODO: use big buffer from htable
380 int accurate_cmd(JCR *jcr)
382 BSOCK *dir = jcr->dir_bsock;
386 if (job_canceled(jcr)) {
389 if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
390 dir->fsend(_("2991 Bad accurate command\n"));
394 jcr->accurate = true;
396 accurate_init(jcr, nb);
399 * buffer = sizeof(CurFile) + dirmsg
400 * dirmsg = fname + \0 + lstat
402 /* get current files */
403 while (dir->recv() >= 0) {
404 len = strlen(dir->msg) + 1;
405 if (len < dir->msglen) {
406 accurate_add_file(jcr, dir->msg, dir->msg + len);
411 extern void *start_heap;
413 char b1[50], b2[50], b3[50], b4[50], b5[50];
414 Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
415 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
416 edit_uint64_with_commas(sm_bytes, b2),
417 edit_uint64_with_commas(sm_max_bytes, b3),
418 edit_uint64_with_commas(sm_buffers, b4),
419 edit_uint64_with_commas(sm_max_buffers, b5));