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=200;
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) {
113 if (!elt->seen || !plugin_check_file(jcr, elt->fname)) {
116 Dmsg2(dbglvl, "base file fname=%s seen=%i\n", elt->fname, elt->seen);
117 ff_pkt->fname = elt->fname;
118 ff_pkt->statp.st_mtime = elt->mtime;
119 ff_pkt->statp.st_ctime = elt->ctime;
120 encode_and_send_attributes(jcr, ff_pkt, stream);
124 term_find_files(ff_pkt);
129 /* This function is called at the end of backup
130 * We walk over all hash disk element, and we check
133 static bool accurate_send_deleted_list(JCR *jcr)
137 int stream = STREAM_UNIX_ATTRIBUTES;
139 if (!jcr->accurate) {
143 if (jcr->file_list == NULL) {
147 ff_pkt = init_find_files();
148 ff_pkt->type = FT_DELETED;
150 foreach_htable(elt, jcr->file_list) {
151 if (elt->seen || plugin_check_file(jcr, elt->fname)) {
154 Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
155 ff_pkt->fname = elt->fname;
156 ff_pkt->statp.st_mtime = elt->mtime;
157 ff_pkt->statp.st_ctime = elt->ctime;
158 encode_and_send_attributes(jcr, ff_pkt, stream);
162 term_find_files(ff_pkt);
166 static void accurate_free(JCR *jcr)
168 if (jcr->file_list) {
169 jcr->file_list->destroy();
170 free(jcr->file_list);
171 jcr->file_list = NULL;
175 /* Send the deleted or the base file list and cleanup */
176 bool accurate_finish(JCR *jcr)
180 if (jcr->get_JobLevel == L_FULL) {
181 ret = accurate_send_base_file_list(jcr);
183 ret = accurate_send_deleted_list(jcr);
191 static bool accurate_add_file(JCR *jcr, char *fname, char *lstat)
197 decode_stat(lstat, &statp, &LinkFIc); /* decode catalog stat */
198 elt.ctime = statp.st_ctime;
199 elt.mtime = statp.st_mtime;
203 /* we store CurFile, fname and ctime/mtime in the same chunk */
204 item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+1);
205 memcpy(item, &elt, sizeof(CurFile));
206 item->fname = (char *)item+sizeof(CurFile);
207 strcpy(item->fname, fname);
208 jcr->file_list->insert(item->fname, item);
210 Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat);
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
220 * Returns: true if file has changed (must be backed up)
221 * false file not changed
223 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
229 if (!jcr->accurate) {
235 if (S_ISDIR(ff_pkt->statp.st_mode)) {
236 fname = ff_pkt->link;
238 fname = ff_pkt->fname;
241 if (!accurate_lookup(jcr, fname, &elt)) {
242 Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
247 if (elt.seen) { /* file has been seen ? */
248 Dmsg1(dbglvl, "accurate %s (already seen)\n", fname);
254 * Loop over options supplied by user and verify the
255 * fields he requests.
257 for (p=Opts_Digest; *p; p++) {
258 char ed1[30], ed2[30];
260 case 'i': /* compare INODEs */
261 if (statc.st_ino != statf.st_ino) {
263 Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
264 edit_uint64((uint64_t)statc.st_ino, ed1),
265 edit_uint64((uint64_t)statf.st_ino, ed2));
266 set_jcr_job_status(jcr, JS_Differences);
269 case 'p': /* permissions bits */
270 if (statc.st_mode != statf.st_mode) {
272 Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
273 (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
274 set_jcr_job_status(jcr, JS_Differences);
277 case 'n': /* number of links */
278 if (statc.st_nlink != statf.st_nlink) {
280 Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
281 (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
282 set_jcr_job_status(jcr, JS_Differences);
285 case 'u': /* user id */
286 if (statc.st_uid != statf.st_uid) {
288 Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
289 (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
290 set_jcr_job_status(jcr, JS_Differences);
293 case 'g': /* group id */
294 if (statc.st_gid != statf.st_gid) {
296 Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
297 (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
298 set_jcr_job_status(jcr, JS_Differences);
302 if (statc.st_size != statf.st_size) {
304 Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
305 edit_uint64((uint64_t)statc.st_size, ed1),
306 edit_uint64((uint64_t)statf.st_size, ed2));
307 set_jcr_job_status(jcr, JS_Differences);
310 case 'a': /* access time */
311 if (statc.st_atime != statf.st_atime) {
313 Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
314 set_jcr_job_status(jcr, JS_Differences);
318 if (statc.st_mtime != statf.st_mtime) {
320 Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
321 set_jcr_job_status(jcr, JS_Differences);
324 case 'c': /* ctime */
325 if (statc.st_ctime != statf.st_ctime) {
327 Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
328 set_jcr_job_status(jcr, JS_Differences);
331 case 'd': /* file size decrease */
332 if (statc.st_size > statf.st_size) {
334 Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
335 edit_uint64((uint64_t)statc.st_size, ed1),
336 edit_uint64((uint64_t)statf.st_size, ed2));
337 set_jcr_job_status(jcr, JS_Differences);
340 case '5': /* compare MD5 */
341 Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
342 do_Digest = CRYPTO_DIGEST_MD5;
344 case '1': /* compare SHA1 */
345 do_Digest = CRYPTO_DIGEST_SHA1;
355 * We check only mtime/ctime like with the normal
356 * incremental/differential mode
358 if (elt.mtime != ff_pkt->statp.st_mtime) {
359 // Jmsg(jcr, M_SAVED, 0, _("%s st_mtime differs\n"), fname);
360 Dmsg3(dbglvl, "%s st_mtime differs (%lld!=%lld)\n",
361 fname, elt.mtime, (utime_t)ff_pkt->statp.st_mtime);
363 } else if (!(ff_pkt->flags & FO_MTIMEONLY)
364 && (elt.ctime != ff_pkt->statp.st_ctime)) {
365 // Jmsg(jcr, M_SAVED, 0, _("%s st_ctime differs\n"), fname);
366 Dmsg1(dbglvl, "%s st_ctime differs\n", fname);
370 accurate_mark_file_as_seen(jcr, &elt);
371 // Dmsg2(dbglvl, "accurate %s = %d\n", fname, stat);
374 unstrip_path(ff_pkt);
379 * TODO: use big buffer from htable
381 int accurate_cmd(JCR *jcr)
383 BSOCK *dir = jcr->dir_bsock;
387 if (job_canceled(jcr)) {
390 if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
391 dir->fsend(_("2991 Bad accurate command\n"));
395 jcr->accurate = true;
397 accurate_init(jcr, nb);
400 * buffer = sizeof(CurFile) + dirmsg
401 * dirmsg = fname + \0 + lstat
403 /* get current files */
404 while (dir->recv() >= 0) {
405 len = strlen(dir->msg) + 1;
406 if (len < dir->msglen) {
407 accurate_add_file(jcr, dir->msg, dir->msg + len);
412 extern void *start_heap;
414 char b1[50], b2[50], b3[50], b4[50], b5[50];
415 Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
416 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
417 edit_uint64_with_commas(sm_bytes, b2),
418 edit_uint64_with_commas(sm_max_bytes, b3),
419 edit_uint64_with_commas(sm_buffers, b4),
420 edit_uint64_with_commas(sm_max_buffers, b5));