1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2016 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero 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 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero 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 /*
24 * Kern Sibbald, MM
25 */
26 /**
27 * @file
28 * This file was derived from GNU TAR source code. Except for a few key
29 * ideas, it has been entirely rewritten for Bareos.
30 *
31 * Thanks to the TAR programmers.
32 */
33
34 #include "include/bareos.h"
35 #include "include/jcr.h"
36 #include "find.h"
37 #include "findlib/match.h"
38 #include "findlib/find_one.h"
39 #include "findlib/hardlink.h"
40 #include "findlib/fstype.h"
41 #include "findlib/drivetype.h"
42
43 #ifdef HAVE_DARWIN_OS
44 #include <sys/param.h>
45 #include <sys/mount.h>
46 #include <sys/attr.h>
47 #endif
48
49 extern int32_t name_max; /* filename max length */
50 extern int32_t path_max; /* path name max length */
51
52 /**
53 * Create a new directory Find File packet, but copy
54 * some of the essential info from the current packet.
55 * However, be careful to zero out the rest of the
56 * packet.
57 */
new_dir_ff_pkt(FindFilesPacket * ff_pkt)58 static inline FindFilesPacket *new_dir_ff_pkt(FindFilesPacket *ff_pkt)
59 {
60 FindFilesPacket *dir_ff_pkt;
61
62 dir_ff_pkt = (FindFilesPacket *)bmalloc(sizeof(FindFilesPacket));
63 memcpy(dir_ff_pkt, ff_pkt, sizeof(FindFilesPacket));
64 dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
65 dir_ff_pkt->link = bstrdup(ff_pkt->link);
66 dir_ff_pkt->sys_fname = GetPoolMemory(PM_FNAME);
67 dir_ff_pkt->included_files_list = NULL;
68 dir_ff_pkt->excluded_files_list = NULL;
69 dir_ff_pkt->excluded_paths_list = NULL;
70 dir_ff_pkt->linkhash = NULL;
71 dir_ff_pkt->fname_save = NULL;
72 dir_ff_pkt->link_save = NULL;
73 dir_ff_pkt->ignoredir_fname = NULL;
74
75 return dir_ff_pkt;
76 }
77
78 /**
79 * Free the temp directory ff_pkt
80 */
FreeDirFfPkt(FindFilesPacket * dir_ff_pkt)81 static void FreeDirFfPkt(FindFilesPacket *dir_ff_pkt)
82 {
83 free(dir_ff_pkt->fname);
84 free(dir_ff_pkt->link);
85 FreePoolMemory(dir_ff_pkt->sys_fname);
86 if (dir_ff_pkt->fname_save) {
87 FreePoolMemory(dir_ff_pkt->fname_save);
88 }
89 if (dir_ff_pkt->link_save) {
90 FreePoolMemory(dir_ff_pkt->link_save);
91 }
92 if (dir_ff_pkt->ignoredir_fname) {
93 FreePoolMemory(dir_ff_pkt->ignoredir_fname);
94 }
95 free(dir_ff_pkt);
96 }
97
98 /**
99 * Check to see if we allow the file system type of a file or directory.
100 * If we do not have a list of file system types, we accept anything.
101 */
102 #if defined(HAVE_WIN32)
AcceptFstype(FindFilesPacket * ff,void * dummy)103 static bool AcceptFstype(FindFilesPacket *ff, void *dummy)
104 {
105 return true;
106 }
107 #else
AcceptFstype(FindFilesPacket * ff,void * dummy)108 static bool AcceptFstype(FindFilesPacket *ff, void *dummy)
109 {
110 int i;
111 char fs[1000];
112 bool accept = true;
113
114 if (ff->fstypes.size()) {
115 accept = false;
116 if (!fstype(ff->fname, fs, sizeof(fs))) {
117 Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
118 } else {
119 for (i = 0; i < ff->fstypes.size(); ++i) {
120 if (bstrcmp(fs, (char *)ff->fstypes.get(i))) {
121 Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
122 accept = true;
123 break;
124 }
125 Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
126 ff->fname, ff->fstypes.get(i));
127 }
128 }
129 }
130
131 return accept;
132 }
133 #endif
134
135 /**
136 * Check to see if we allow the drive type of a file or directory.
137 * If we do not have a list of drive types, we accept anything.
138 */
139 #if defined(HAVE_WIN32)
AcceptDrivetype(FindFilesPacket * ff,void * dummy)140 static inline bool AcceptDrivetype(FindFilesPacket *ff, void *dummy)
141 {
142 int i;
143 char dt[100];
144 bool accept = true;
145
146 if (ff->drivetypes.size()) {
147 accept = false;
148 if (!Drivetype(ff->fname, dt, sizeof(dt))) {
149 Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
150 } else {
151 for (i = 0; i < ff->drivetypes.size(); ++i) {
152 if (bstrcmp(dt, (char *)ff->drivetypes.get(i))) {
153 Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
154 accept = true;
155 break;
156 }
157 Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
158 ff->fname, ff->drivetypes.get(i));
159 }
160 }
161 }
162
163 return accept;
164 }
165 #else
AcceptDrivetype(FindFilesPacket * ff,void * dummy)166 static inline bool AcceptDrivetype(FindFilesPacket *ff, void *dummy)
167 {
168 return true;
169 }
170 #endif
171
172 /**
173 * This function determines whether we can use getattrlist()
174 * It's odd, but we have to use the function to determine that...
175 * Also, the man pages talk about things as if they were implemented.
176 *
177 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
178 * volumes, which makes me trust it is OK for others, too.
179 */
VolumeHasAttrlist(const char * fname)180 static bool VolumeHasAttrlist(const char *fname)
181 {
182 #ifdef HAVE_DARWIN_OS
183 struct statfs st;
184 struct volinfo_struct {
185 unsigned long length; /* Mandatory field */
186 vol_capabilities_attr_t info; /* Volume capabilities */
187 } vol;
188 struct attrlist attrList;
189
190 memset(&attrList, 0, sizeof(attrList));
191 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
192 attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
193 if (statfs(fname, &st) == 0) {
194 /*
195 * We need to check on the mount point
196 */
197 if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0 &&
198 (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST) &&
199 (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
200 return true;
201 }
202 }
203 #endif
204 return false;
205 }
206
207 /**
208 * check for BSD nodump flag
209 */
no_dump(JobControlRecord * jcr,FindFilesPacket * ff_pkt)210 static inline bool no_dump(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
211 {
212 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
213 if (BitIsSet(FO_HONOR_NODUMP, ff_pkt->flags) &&
214 (ff_pkt->statp.st_flags & UF_NODUMP) ) {
215 Jmsg(jcr, M_INFO, 1, _(" NODUMP flag set - will not process %s\n"),
216 ff_pkt->fname);
217 return true; /* do not backup this file */
218 }
219 #endif
220 return false; /* do backup */
221 }
222
223 /**
224 * check for sizes
225 */
CheckSizeMatching(JobControlRecord * jcr,FindFilesPacket * ff_pkt)226 static inline bool CheckSizeMatching(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
227 {
228 int64_t begin_size, end_size, difference;
229
230 /*
231 * See if size matching is turned on.
232 */
233 if (!ff_pkt->size_match) {
234 return true;
235 }
236
237 /*
238 * Loose the unsigned bits to keep the compiler from warning
239 * about comparing signed and unsigned. As a size of a file
240 * can only be positive the unsigned is not really to interesting.
241 */
242 begin_size = ff_pkt->size_match->begin_size;
243 end_size = ff_pkt->size_match->end_size;
244
245 /*
246 * See what kind of matching should be done.
247 */
248 switch (ff_pkt->size_match->type) {
249 case size_match_approx:
250 /*
251 * Calculate the fraction this size is of the wanted size.
252 */
253 if ((int64_t)ff_pkt->statp.st_size > begin_size) {
254 difference = ff_pkt->statp.st_size - begin_size;
255 } else {
256 difference = begin_size - ff_pkt->statp.st_size;
257 }
258
259 /*
260 * See if the difference is less then 1% of the total.
261 */
262 return (difference < (begin_size / 100));
263 case size_match_smaller:
264 return (int64_t)ff_pkt->statp.st_size < begin_size;
265 case size_match_greater:
266 return (int64_t)ff_pkt->statp.st_size > begin_size;
267 case size_match_range:
268 return ((int64_t)ff_pkt->statp.st_size >= begin_size) &&
269 ((int64_t)ff_pkt->statp.st_size <= end_size);
270 default:
271 return true;
272 }
273 }
274
275 /**
276 * Check if a file have changed during backup and display an error
277 */
HasFileChanged(JobControlRecord * jcr,FindFilesPacket * ff_pkt)278 bool HasFileChanged(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
279 {
280 struct stat statp;
281 Dmsg1(500, "HasFileChanged fname=%s\n",ff_pkt->fname);
282
283 if (ff_pkt->type != FT_REG) { /* not a regular file */
284 return false;
285 }
286
287 if (lstat(ff_pkt->fname, &statp) != 0) {
288 BErrNo be;
289 Jmsg(jcr, M_WARNING, 0,
290 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
291 return true;
292 }
293
294 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
295 Jmsg(jcr, M_ERROR, 0, _("%s: mtime changed during backup.\n"), ff_pkt->fname);
296 Dmsg3(50, "%s mtime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
297 (int64_t)ff_pkt->statp.st_mtime, (int64_t)statp.st_mtime);
298 return true;
299 }
300
301 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
302 Jmsg(jcr, M_ERROR, 0, _("%s: ctime changed during backup.\n"), ff_pkt->fname);
303 Dmsg3(50, "%s ctime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
304 (int64_t)ff_pkt->statp.st_ctime, (int64_t)statp.st_ctime);
305 return true;
306 }
307
308 if (statp.st_size != ff_pkt->statp.st_size) {
309 /* TODO: add size change */
310 Jmsg(jcr, M_ERROR, 0, _("%s: size changed during backup.\n"),ff_pkt->fname);
311 Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
312 (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
313 return true;
314 }
315
316 if ((statp.st_blksize != ff_pkt->statp.st_blksize) ||
317 (statp.st_blocks != ff_pkt->statp.st_blocks)) {
318 Jmsg(jcr, M_ERROR, 0, _("%s: size changed during backup.\n"),ff_pkt->fname);
319 Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
320 (int64_t)ff_pkt->statp.st_blocks, (int64_t)statp.st_blocks);
321 return true;
322 }
323
324 return false;
325 }
326
327 /**
328 * For incremental/diffential or accurate backups, we
329 * determine if the current file has changed.
330 */
CheckChanges(JobControlRecord * jcr,FindFilesPacket * ff_pkt)331 bool CheckChanges(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
332 {
333 /*
334 * In special mode (like accurate backup), the programmer can
335 * choose his comparison function.
336 */
337 if (ff_pkt->CheckFct) {
338 return ff_pkt->CheckFct(jcr, ff_pkt);
339 }
340
341 /*
342 * For normal backups (incr/diff), we use this default behaviour
343 */
344 if (ff_pkt->incremental &&
345 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
346 (BitIsSet(FO_MTIMEONLY, ff_pkt->flags) ||
347 ff_pkt->statp.st_ctime < ff_pkt->save_time))) {
348 return false;
349 }
350
351 return true;
352 }
353
HaveIgnoredir(FindFilesPacket * ff_pkt)354 static inline bool HaveIgnoredir(FindFilesPacket *ff_pkt)
355 {
356 struct stat sb;
357 char *ignoredir;
358
359 /*
360 * Ensure that pointers are defined
361 */
362 if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
363 return false;
364 }
365
366 for (int i = 0; i < ff_pkt->fileset->incexe->ignoredir.size(); i++) {
367 ignoredir = (char *)ff_pkt->fileset->incexe->ignoredir.get(i);
368
369 if (ignoredir) {
370 if (!ff_pkt->ignoredir_fname) {
371 ff_pkt->ignoredir_fname = GetPoolMemory(PM_FNAME);
372 }
373 Mmsg(ff_pkt->ignoredir_fname, "%s/%s", ff_pkt->fname, ignoredir);
374 if (stat(ff_pkt->ignoredir_fname, &sb) == 0) {
375 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
376 ff_pkt->fname, ignoredir);
377 return true; /* Just ignore this directory */
378 }
379 }
380 }
381
382 return false;
383 }
384
385 /**
386 * Restore file times.
387 */
RestoreFileTimes(FindFilesPacket * ff_pkt,char * fname)388 static inline void RestoreFileTimes(FindFilesPacket *ff_pkt, char *fname)
389 {
390 #if defined(HAVE_LUTIMES)
391 struct timeval restore_times[2];
392
393 restore_times[0].tv_sec = ff_pkt->statp.st_atime;
394 restore_times[0].tv_usec = 0;
395 restore_times[1].tv_sec = ff_pkt->statp.st_mtime;
396 restore_times[1].tv_usec = 0;
397
398 lutimes(fname, restore_times);
399 #elif defined(HAVE_UTIMES)
400 struct timeval restore_times[2];
401
402 restore_times[0].tv_sec = ff_pkt->statp.st_atime;
403 restore_times[0].tv_usec = 0;
404 restore_times[1].tv_sec = ff_pkt->statp.st_mtime;
405 restore_times[1].tv_usec = 0;
406
407 utimes(fname, restore_times);
408 #else
409 struct utimbuf restore_times;
410
411 restore_times.actime = ff_pkt->statp.st_atime;
412 restore_times.modtime = ff_pkt->statp.st_mtime;
413
414 utime(fname, &restore_times);
415 #endif
416 }
417
418 #ifdef HAVE_DARWIN_OS
419 /**
420 * Handling of a HFS+ attributes.
421 */
process_hfsattributes(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)422 static inline int process_hfsattributes(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
423 int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
424 char *fname, bool top_level)
425 {
426 /*
427 * TODO: initialise attrList once elsewhere?
428 */
429 struct attrlist attrList;
430
431 memset(&attrList, 0, sizeof(attrList));
432 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
433 attrList.commonattr = ATTR_CMN_FNDRINFO;
434 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
435 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
436 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
437 ff_pkt->type = FT_NOSTAT;
438 ff_pkt->ff_errno = errno;
439 return HandleFile(jcr, ff_pkt, top_level);
440 }
441
442 return -1;
443 }
444 #endif
445
446 /**
447 * Handling of a hardlinked file.
448 */
process_hardlink(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level,bool * done)449 static inline int process_hardlink(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
450 int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
451 char *fname, bool top_level, bool *done)
452 {
453 int rtn_stat = 0;
454 CurLink *hl;
455
456 hl = lookup_hardlink(jcr, ff_pkt, ff_pkt->statp.st_ino, ff_pkt->statp.st_dev);
457 if (hl) {
458 /*
459 * If we have already backed up the hard linked file don't do it again
460 */
461 if (bstrcmp(hl->name, fname)) {
462 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", hl->FileIndex, fname);
463 *done = true;
464 return 1; /* ignore */
465 }
466
467 ff_pkt->link = hl->name;
468 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
469 ff_pkt->LinkFI = hl->FileIndex;
470 ff_pkt->linked = NULL;
471 ff_pkt->digest = hl->digest;
472 ff_pkt->digest_stream = hl->digest_stream;
473 ff_pkt->digest_len = hl->digest_len;
474
475 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
476 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n", ff_pkt->FileIndex, hl->FileIndex, hl->name);
477 *done = true;
478 } else {
479 /*
480 * File not previously dumped. Chain it into our list.
481 */
482 hl = new_hardlink(jcr, ff_pkt, fname, ff_pkt->statp.st_ino, ff_pkt->statp.st_dev);
483 ff_pkt->linked = hl; /* Mark saved link */
484 Dmsg2(400, "Added to hash FI=%d file=%s\n", ff_pkt->FileIndex, hl->name);
485 *done = false;
486 }
487
488 return rtn_stat;
489 }
490
491 /**
492 * Handling of a regular file.
493 */
process_regular_file(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)494 static inline int process_regular_file(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
495 int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
496 char *fname, bool top_level)
497 {
498 int rtn_stat;
499 boffset_t sizeleft;
500
501 sizeleft = ff_pkt->statp.st_size;
502
503 /*
504 * Don't bother opening empty, world readable files. Also do not open
505 * files when archive is meant for /dev/null.
506 */
507 if (ff_pkt->null_output_device || (sizeleft == 0 &&
508 MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
509 ff_pkt->type = FT_REGE;
510 } else {
511 ff_pkt->type = FT_REG;
512 }
513
514 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
515 if (ff_pkt->linked) {
516 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
517 }
518
519 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n",
520 ff_pkt->FileIndex, ff_pkt->linked ? 1 : 0, fname);
521
522 if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
523 RestoreFileTimes(ff_pkt, fname);
524 }
525
526 return rtn_stat;
527 }
528
529 /**
530 * Handling of a symlink.
531 */
process_symlink(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)532 static inline int process_symlink(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
533 int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
534 char *fname, bool top_level)
535 {
536 int rtn_stat;
537 int size;
538 char *buffer = (char *)alloca(path_max + name_max + 102);
539
540 size = readlink(fname, buffer, path_max + name_max + 101);
541 if (size < 0) {
542 /*
543 * Could not follow link
544 */
545 ff_pkt->type = FT_NOFOLLOW;
546 ff_pkt->ff_errno = errno;
547 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
548 if (ff_pkt->linked) {
549 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
550 }
551 return rtn_stat;
552 }
553
554 buffer[size] = 0;
555 ff_pkt->link = buffer; /* point to link */
556 ff_pkt->type = FT_LNK; /* got a real link */
557
558 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
559 if (ff_pkt->linked) {
560 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
561 }
562
563 return rtn_stat;
564 }
565
566 /**
567 * Handling of a directory.
568 */
process_directory(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,dev_t parent_device,bool top_level)569 static inline int process_directory(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
570 int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
571 char *fname, dev_t parent_device, bool top_level)
572 {
573 int rtn_stat;
574 DIR *directory;
575 struct dirent *result;
576 #ifdef USE_READDIR_R
577 struct dirent *entry;
578 #endif
579 char *link;
580 int link_len;
581 int len;
582 dev_t our_device = ff_pkt->statp.st_dev;
583 bool recurse = true;
584 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
585
586 /*
587 * Ignore this directory and everything below if the file .nobackup
588 * (or what is defined for IgnoreDir in this fileset) exists
589 */
590 if (HaveIgnoredir(ff_pkt)) {
591 return 1; /* Just ignore this directory */
592 }
593
594 /*
595 * Build a canonical directory name with a trailing slash in link var
596 */
597 len = strlen(fname);
598 link_len = len + 200;
599 link = (char *)bmalloc(link_len + 2);
600 bstrncpy(link, fname, link_len);
601
602 /*
603 * Strip all trailing slashes
604 */
605 while (len >= 1 && IsPathSeparator(link[len - 1])) {
606 len--;
607 }
608 link[len++] = '/'; /* add back one */
609 link[len] = 0;
610
611 ff_pkt->link = link;
612 if (!CheckChanges(jcr, ff_pkt)) {
613 /*
614 * Incremental/Full+Base option, directory entry not changed
615 */
616 ff_pkt->type = FT_DIRNOCHG;
617 } else {
618 ff_pkt->type = FT_DIRBEGIN;
619 }
620
621 bool is_win32_mount_point = false;
622 #if defined(HAVE_WIN32)
623 is_win32_mount_point = ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT;
624
625 if (ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_REPARSE_POINT) {
626 ff_pkt->type = FT_REPARSE;
627 }
628
629 /* treat win32 mount points (Volume Mount Points) as directories */
630 if (is_win32_mount_point) {
631 ff_pkt->type = FT_DIRBEGIN;
632 }
633 #endif
634
635 /*
636 * Note, we return the directory to the calling program (HandleFile)
637 * when we first see the directory (FT_DIRBEGIN.
638 * This allows the program to apply matches and make a
639 * choice whether or not to accept it. If it is accepted, we
640 * do not immediately save it, but do so only after everything
641 * in the directory is seen (i.e. the FT_DIREND).
642 */
643 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
644 if (rtn_stat < 1 ||
645 ff_pkt->type == FT_REPARSE) { /* ignore or error status */
646 free(link);
647
648 return rtn_stat;
649 }
650
651 /*
652 * Done with DIRBEGIN, next call will be DIREND
653 */
654 if (ff_pkt->type == FT_DIRBEGIN) {
655 ff_pkt->type = FT_DIREND;
656 }
657
658 /*
659 * Create a temporary ff packet for this directory
660 * entry, and defer handling the directory until
661 * we have recursed into it. This saves the
662 * directory after all files have been processed, and
663 * during the restore, the directory permissions will
664 * be reset after all the files have been restored.
665 */
666 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
667 FindFilesPacket *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
668
669 /*
670 * Do not descend into subdirectories (recurse) if the
671 * user has turned it off for this directory.
672 *
673 * If we are crossing file systems, we are either not allowed
674 * to cross, or we may be restricted by a list of permitted
675 * file systems.
676 */
677 if (!top_level && BitIsSet(FO_NO_RECURSION, ff_pkt->flags)) {
678 ff_pkt->type = FT_NORECURSE;
679 recurse = false;
680 } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
681 is_win32_mount_point)) {
682 if(!BitIsSet(FO_MULTIFS, ff_pkt->flags)) {
683 ff_pkt->type = FT_NOFSCHG;
684 recurse = false;
685 } else if (!AcceptFstype(ff_pkt, NULL)) {
686 ff_pkt->type = FT_INVALIDFS;
687 recurse = false;
688 } else {
689 ff_pkt->volhas_attrlist = VolumeHasAttrlist(fname);
690 }
691 }
692
693 /*
694 * If not recursing, just backup dir and return
695 */
696 if (!recurse) {
697 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
698 if (ff_pkt->linked) {
699 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
700 }
701 free(link);
702 FreeDirFfPkt(dir_ff_pkt);
703 ff_pkt->link = ff_pkt->fname; /* reset "link" */
704 if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
705 RestoreFileTimes(ff_pkt, fname);
706 }
707 return rtn_stat;
708 }
709
710 ff_pkt->link = ff_pkt->fname; /* reset "link" */
711
712 /*
713 * Descend into or "recurse" into the directory to read all the files in it.
714 */
715 errno = 0;
716 if ((directory = opendir(fname)) == NULL) {
717 ff_pkt->type = FT_NOOPEN;
718 ff_pkt->ff_errno = errno;
719 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
720 if (ff_pkt->linked) {
721 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
722 }
723 free(link);
724 FreeDirFfPkt(dir_ff_pkt);
725 return rtn_stat;
726 }
727
728 /*
729 * Process all files in this directory entry (recursing).
730 * This would possibly run faster if we chdir to the directory
731 * before traversing it.
732 */
733 rtn_stat = 1;
734
735 /*
736 * Allocate some extra room so an overflow of the d_name with more then
737 * name_max bytes doesn't kill us right away. We check in the loop if
738 * an overflow has not happened.
739 */
740 #ifdef USE_READDIR_R
741 int status;
742
743 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
744 while (!JobCanceled(jcr)) {
745 int name_length;
746
747 status = Readdir_r(directory, entry, &result);
748 if (status != 0 || result == NULL) {
749 break;
750 }
751
752 name_length = (int)NAMELEN(entry);
753
754 /*
755 * Some filesystems violate against the rules and return filenames
756 * longer than _PC_NAME_MAX. Log the error and continue.
757 */
758 if ((name_max + 1) <= ((int)sizeof(struct dirent) + name_length)) {
759 Jmsg2(jcr, M_ERROR, 0, _("%s: File name too long [%d]\n"), entry->d_name, name_length);
760 continue;
761 }
762
763 /*
764 * Skip `.', `..', and excluded file names.
765 */
766 if (entry->d_name[0] == '\0' ||
767 (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' ||
768 (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))) {
769 continue;
770 }
771
772 /*
773 * Make sure there is enough room to store the whole name.
774 */
775 if (name_length + len >= link_len) {
776 link_len = len + name_length + 1;
777 link = (char *)brealloc(link, link_len + 1);
778 }
779
780 memcpy(link + len, entry->d_name, name_length);
781 link[len + name_length] = '\0';
782
783 if (!FileIsExcluded(ff_pkt, link)) {
784 rtn_stat = FindOneFile(jcr, ff_pkt, HandleFile, link, our_device, false);
785 if (ff_pkt->linked) {
786 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
787 }
788 }
789 }
790
791 closedir(directory);
792 free(link);
793 free(entry);
794
795 #else
796
797 while (!JobCanceled(jcr)) {
798 int name_length;
799 result = readdir(directory);
800 if (result == NULL) {
801 break;
802 }
803
804 name_length = (int)NAMELEN(result);
805
806 /*
807 * Some filesystems violate against the rules and return filenames
808 * longer than _PC_NAME_MAX. Log the error and continue.
809 */
810 if ((name_max + 1) <= ((int)sizeof(struct dirent) + name_length)) {
811 Jmsg2(jcr, M_ERROR, 0, _("%s: File name too long [%d]\n"), result->d_name, name_length);
812 continue;
813 }
814
815 /*
816 * Skip `.', `..', and excluded file names.
817 */
818 if (result->d_name[0] == '\0' ||
819 (result->d_name[0] == '.' && (result->d_name[1] == '\0' ||
820 (result->d_name[1] == '.' && result->d_name[2] == '\0')))) {
821 continue;
822 }
823
824 /*
825 * Make sure there is enough room to store the whole name.
826 */
827 if (name_length + len >= link_len) {
828 link_len = len + name_length + 1;
829 link = (char *)brealloc(link, link_len + 1);
830 }
831
832 memcpy(link + len, result->d_name, name_length);
833 link[len + name_length] = '\0';
834
835 if (!FileIsExcluded(ff_pkt, link)) {
836 rtn_stat = FindOneFile(jcr, ff_pkt, HandleFile, link, our_device, false);
837 if (ff_pkt->linked) {
838 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
839 }
840 }
841 }
842
843 closedir(directory);
844 free(link);
845 #endif
846 /*
847 * Now that we have recursed through all the files in the
848 * directory, we "save" the directory so that after all
849 * the files are restored, this entry will serve to reset
850 * the directory modes and dates. Temp directory values
851 * were used without this record.
852 */
853 HandleFile(jcr, dir_ff_pkt, top_level); /* handle directory entry */
854 if (ff_pkt->linked) {
855 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
856 }
857 FreeDirFfPkt(dir_ff_pkt);
858
859 if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
860 RestoreFileTimes(ff_pkt, fname);
861 }
862 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
863
864 return rtn_stat;
865 }
866
867 /**
868 * Handling of a special file.
869 */
process_special_file(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)870 static inline int process_special_file(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
871 int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
872 char *fname, bool top_level)
873 {
874 int rtn_stat;
875
876 /*
877 * If it is explicitly mentioned (i.e. top_level) and is
878 * a block device, we do a raw backup of it or if it is
879 * a fifo, we simply read it.
880 */
881 #ifdef HAVE_FREEBSD_OS
882 /*
883 * On FreeBSD, all block devices are character devices, so
884 * to be able to read a raw disk, we need the check for
885 * a character device.
886 *
887 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
888 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
889 */
890 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
891 #else
892 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
893 #endif
894 ff_pkt->type = FT_RAW; /* raw partition */
895 } else if (top_level &&
896 S_ISFIFO(ff_pkt->statp.st_mode) &&
897 BitIsSet(FO_READFIFO, ff_pkt->flags)) {
898 ff_pkt->type = FT_FIFO;
899 } else {
900 /*
901 * The only remaining types are special (character, ...) files
902 */
903 ff_pkt->type = FT_SPEC;
904 }
905
906 rtn_stat = HandleFile(jcr, ff_pkt, top_level);
907 if (ff_pkt->linked) {
908 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
909 }
910
911 return rtn_stat;
912 }
913
914 /**
915 * See if we need to perform any processing for a given file.
916 */
917 static inline bool NeedsProcessing(JobControlRecord *jcr, FindFilesPacket *ff_pkt, char *fname)
918 {
919 int loglevel = M_INFO;
920
921 if (!AcceptFstype(ff_pkt, NULL)) {
922 char fs[100];
923
924 ff_pkt->type = FT_INVALIDFS;
925 if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
926 RestoreFileTimes(ff_pkt, fname);
927 }
928
929 if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
930 bstrncpy(fs, "unknown", sizeof(fs));
931 loglevel = M_WARNING;
932 }
933
934 Jmsg(jcr, loglevel, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
935 return false; /* Just ignore this error - or the whole backup is cancelled */
936 }
937
938 if (!AcceptDrivetype(ff_pkt, NULL)) {
939 char dt[100];
940
941 ff_pkt->type = FT_INVALIDDT;
942 if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
943 RestoreFileTimes(ff_pkt, fname);
944 }
945
946 if (!Drivetype(ff_pkt->fname, dt, sizeof(dt))) {
947 bstrncpy(dt, "unknown", sizeof(dt));
948 loglevel = M_WARNING;
949 }
950
951 Jmsg(jcr, loglevel, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
952 return false; /* Just ignore this error - or the whole backup is cancelled */
953 }
954
955 ff_pkt->volhas_attrlist = VolumeHasAttrlist(fname);
956
957 return true;
958 }
959
960 /**
961 * Find a single file.
962 *
963 * HandleFile is the callback for handling the file.
964 * p is the filename
965 * parent_device is the device we are currently on
966 * top_level is 1 when not recursing or 0 when descending into a directory.
967 */
968 int FindOneFile(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
969 int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
970 char *fname, dev_t parent_device, bool top_level)
971 {
972 int rtn_stat;
973 bool done = false;
974
975 ff_pkt->fname = ff_pkt->link = fname;
976 ff_pkt->type = FT_UNSET;
977 if (lstat(fname, &ff_pkt->statp) != 0) {
978 /*
979 * Cannot stat file
980 */
981 ff_pkt->type = FT_NOSTAT;
982 ff_pkt->ff_errno = errno;
983 return HandleFile(jcr, ff_pkt, top_level);
984 }
985
986 Dmsg1(300, "File ----: %s\n", fname);
987
988 /*
989 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
990 */
991 if (top_level) {
992 if (!NeedsProcessing(jcr, ff_pkt, fname)) {
993 return 1;
994 }
995 }
996
997 /*
998 * Ignore this entry if no_dump() returns true
999 */
1000 if (no_dump(jcr, ff_pkt)) {
1001 Dmsg1(100, "'%s' ignored (NODUMP flag set)\n", ff_pkt->fname);
1002 return 1;
1003 }
1004
1005 switch (ff_pkt->statp.st_mode & S_IFMT) {
1006 case S_IFDIR:
1007 break;
1008 case S_IFREG:
1009 if (!CheckSizeMatching(jcr, ff_pkt)) {
1010 Dmsg1(100, "'%s' ignored (Size doesn't match\n", ff_pkt->fname);
1011 return 1;
1012 }
1013 /*
1014 * Fall Through
1015 */
1016 default:
1017 /*
1018 * If this is an Incremental backup, see if file was modified
1019 * since our last "save_time", presumably the last Full save
1020 * or Incremental.
1021 */
1022 if (!CheckChanges(jcr, ff_pkt)) {
1023 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
1024 ff_pkt->type = FT_NOCHG;
1025 return HandleFile(jcr, ff_pkt, top_level);
1026 }
1027 break;
1028 }
1029
1030 #ifdef HAVE_DARWIN_OS
1031 if (BitIsSet(FO_HFSPLUS, ff_pkt->flags) &&
1032 ff_pkt->volhas_attrlist &&
1033 S_ISREG(ff_pkt->statp.st_mode)) {
1034 rtn_stat = process_hfsattributes(jcr, ff_pkt, HandleFile, fname, top_level);
1035 if (rtn_stat != -1) {
1036 return rtn_stat;
1037 }
1038 }
1039 #endif
1040
1041 ff_pkt->LinkFI = 0;
1042 /*
1043 * Handle hard linked files
1044 *
1045 * Maintain a list of hard linked files already backed up. This
1046 * allows us to ensure that the data of each file gets backed
1047 * up only once.
1048 */
1049 if (!BitIsSet(FO_NO_HARDLINK, ff_pkt->flags) && ff_pkt->statp.st_nlink > 1) {
1050 switch (ff_pkt->statp.st_mode & S_IFMT) {
1051 case S_IFREG:
1052 case S_IFCHR:
1053 case S_IFBLK:
1054 case S_IFIFO:
1055 #ifdef S_IFSOCK
1056 case S_IFSOCK:
1057 #endif
1058 /*
1059 * Via the done variable the process_hardlink function returns
1060 * if file processing is done. If done is set to false we continue
1061 * with the normal processing of the file.
1062 */
1063 rtn_stat = process_hardlink(jcr, ff_pkt, HandleFile, fname, top_level, &done);
1064 if (done) {
1065 return rtn_stat;
1066 }
1067 break;
1068 default:
1069 ff_pkt->linked = NULL;
1070 break;
1071 }
1072 } else {
1073 ff_pkt->linked = NULL;
1074 }
1075
1076 /*
1077 * Based on the type of file call the correct function.
1078 * This is not a link to a previously dumped file, so dump it.
1079 */
1080 switch (ff_pkt->statp.st_mode & S_IFMT) {
1081 case S_IFREG:
1082 return process_regular_file(jcr, ff_pkt, HandleFile, fname, top_level);
1083 #ifdef S_IFLNK
1084 case S_IFLNK:
1085 return process_symlink(jcr, ff_pkt, HandleFile, fname, top_level);
1086 #endif
1087 case S_IFDIR:
1088 return process_directory(jcr, ff_pkt, HandleFile, fname, parent_device, top_level);
1089 default:
1090 return process_special_file(jcr, ff_pkt, HandleFile, fname, top_level);
1091 }
1092 }
1093
1094 int TermFindOne(FindFilesPacket *ff)
1095 {
1096 int count;
1097
1098 if (ff->linkhash == NULL) {
1099 return 0;
1100 }
1101
1102 count = ff->linkhash->size();
1103 ff->linkhash->destroy();
1104 free(ff->linkhash);
1105 ff->linkhash = NULL;
1106
1107 return count;
1108 }
1109